旧的 JaxB 和 JDK8 元空间 OutOfMemory 问题

我们正在开发一个商业应用程序(100多万个LOC),这是自10多年来开发的。在切换到 JDK8 时,我们遇到了 JDK8 元空间的问题。这似乎与 com.sun..xml.ws:webservices-rt:1.4 (Metro 1.4) 中引用的 JaxB 版本有关。由于应用程序中的密集链接以及通过JaxB创建类/实例的旧式创建,因此动态切换旧库并不简单。

目前我们正在研究这个问题。我们创建了一个重现此行为的示例程序:

import java.io.ByteArrayInputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class X
{
  private static final String XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><x test=\"test\" />";

  @XmlAttribute
  String test;

  public static void main( String[] args ) throws JAXBException, InterruptedException
  {
    System.out.println("start");

    while ( true )
    {
      JAXBContext jc = JAXBContext.newInstance( X.class );
      Unmarshaller unmarshaller = jc.createUnmarshaller();
      X object = (X) unmarshaller.unmarshal( new ByteArrayInputStream( XML.getBytes() ) );
      System.out.println( object.test );
    }
  }
}

JDK7保持了PermGenSpace的清洁。(用16M PermGen模拟)使用 JDK7 运行的记忆

使用 JDK8,应用程序运行缓慢到 OOM 异常。VisualVM 捕获异常,并使进程在可用 Metaspace 的最大值上运行。即使在这里,它在max上运行了相当长一段时间后也会卡住(用16M元空间模拟)使用JDK8运行的内存

有没有人有一些想法如何获得垃圾回收器的遗留行为,所以我们不会遇到那些内存不足的问题?或者您有任何其他想法如何处理这个问题?

谢谢。

edit1: 运行参数 JDK7:

-XX:+TraceClassLoading -XX:+TraceClassUnloading -XX:MaxPermSize=16M -XX:PermSize=1M -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError

=> 不创建堆转储

运行参数 JDK8:

-XX:+TraceClassLoading -XX:+TraceClassUnloading -XX:MaxMetaspaceSize=16M -XX:MetaspaceSize=1M -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError

=>运行时生成堆转储。

VisualVM 的可用内存不显示实际的最大元空间值。如果不加以限制,元空间就会不断增加,直到超出内存。

编辑 2:

我已经尝试了JDK8的所有可用垃圾回收器。他们都有同样的问题。

编辑3:

在我们的实际应用中,通过交换库来解决是很困难的,因为 JAXB 和我们应用程序的几个模块之间耦合很大。因此,短期内需要修复垃圾回收器行为。从长远来看,已经计划好了修复。


答案 1

我们解决了当前问题,直到能够使用以下 VM 参数修复应用程序中的所有问题:

-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true

我希望这将有助于其他有类似问题的其他人...


答案 2

这是Gary正在谈论的解决方案,这比仅仅设置一个标志要好(因为即使是JAXB的人也建议让它成为单例...)

private static Map<class<?>, JAXBContext> contextStore = new ConcurrentHashMap<class<?>, JAXBContext>();
... 
protected static JAXBContext getContextInstance(Class<?> objectClass) throws JAXBException{
  JAXBContext context = contextStore.get(objectClass);
  if (context==null){
    context = JAXBContext.newInstance(objectClass);
    contextStore.put(objectClass, context);
  }
  return context;
}

//using it like this:
JAXBContext context = getContextInstance(objectClass);

JAXB-564 Bug + 修复程序可以在这里找到。灵感来自早已消失的焦炭博客


推荐