旧的JaxB和JDK8 Metaspace OutOfMemory问题

时间:2015-10-21 09:28:08

标签: java jaxb java-8 out-of-memory java-metro-framework

我们正在开发自10年以来开发的业务应用程序(100万+ LOC)。在切换到JDK8时,我们遇到了JDK8的元空间问题。这似乎与com.sun.xml.ws:webservices-rt:1.4(Metro 1.4)中引用的JaxB-Version有关。由于应用程序中的密切链接以及通过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模拟) Memory of run with JDK7

使用JDK8,应用程序运行缓慢到OOM异常。 VisualVM捕获异常并使进程在最大可用Metaspace上运行。即使在这里,它在最长时间运行了很长一段时间后才会被卡住。 (用16M Metaspace模拟) Memory of run with JDK8

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

感谢。

EDIT1: 运行参数JDK7:

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

=&GT;没有创建堆转储

运行参数JDK8:

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

=&GT;运行时会生成堆转储。

VisualVM的可用内存未显示实际的最大元空间值。如果不受限制,则元空间不断增加,直到超过内存。

编辑2:

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

编辑3:

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

3 个答案:

答案 0 :(得分:26)

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

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

我希望这会帮助其他类似问题的人......

答案 1 :(得分:3)

JAXBContext.newInstance()应该使用一次来为您的类创建一个解组的上下文。否则它会耗尽你的permgen或元空间。

答案 2 :(得分:0)

这是加里(Gary)谈论的解决方案,这比仅仅设置一个标志要好(因为甚至JAXB guys都建议将其设置为单例...)

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);