旧版JaxB和JDK8 Metaspace内存溢出问题

29
我们正在开发一款商业应用程序(1百万+ 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的最大值上保持进程运行。即使在这里也会在运行一段时间后陷入困境。 (使用16M Metaspace模拟) JDK8运行内存

有人有想法如何获得垃圾收集器的传统行为,以便我们不会遇到这些内存不足的问题吗?或者您有任何其他处理此问题的想法吗?

谢谢。

编辑1: 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的可用内存不显示真正的最大metaspace值。如果不受限制,则metaspace会不断增加,直到超出内存为止。

编辑2:

我尝试了JDK8中所有可用的垃圾收集器。它们都有同样的问题。

编辑3:

在我们的实际应用程序中,通过交换库来解决问题非常困难,因为JAXB与我们应用程序的几个模块之间的耦合很重。因此,短期内需要修复垃圾收集器行为。长期的正确修复已经计划好了。


VisualVM捕获异常并保持进程运行-我从未知道VisualVM可以做到这一点。除此之外,你的截图显示在你的Java8运行中,Metaspace有一个1GB的限制,但保持在16MB,远离OOME的运行。Metaspace似乎没有被收集,但也有一个简单、明显的原因:在Java 8设置中,它保留类,计数器始终保持在约4300个类,而在Java 7运行中,已计数了惊人的约43,000个类。所以,在那里,类似乎不断重新生成... - Holger
OOM错误会在运行时在Eclipse调试器中显示。任务受运行配置参数的限制,Metaspace仅限于16MB。这不适用于VisualVM实例。因此,其余内存量将被占用。如果没有使用VisualVM,让应用程序保持运行状态,那么当JDK8进程达到16MB Metaspace时,OOM将停止该进程。使用webservices-rt 1.6.1或删除它并使用JDK8 JaxB-implementation,示例程序将保持恒定的12.5MB Metaspace。如果不限制JDK8的内存,它将增加到所有可用为止。 - The_Gentleman
我有点困惑。这些截图展示了什么?你是在分析你的应用程序还是VisualVM? - Holger
抱歉,但是您的解释仍然令人困惑。特别是句子“当使用例如webservices-rt 1.6.1或删除它并使用JDK8 JaxB实现时,示例程序保持12.5MB Metaspace的恒定数量。”这种设置与后续的“If not limiting JDK8 Memory it will increase untill all is used”有何不同?而且,您现在是在谈论“Memory”还是“Meta-Space”? - Holger
4
仅在使用Java 8时才会出现内存泄漏问题,而在Java 7中,垃圾回收器可以无问题地清理未使用的类。正如问题所述,对于我们来说,更换JaxB版本只是从长远来看可行的解决方案。 - The_Gentleman
显示剩余3条评论
3个回答

35

我们通过使用以下VM参数解决了我们目前的问题,直到能够修复应用程序中所有发生的情况:

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

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


1
我们也遇到了类似的问题,当我们将应用程序从JDK7切换到JDK8时出现了OOME相关的问题,而这个参数解决了我们的问题。感谢您发布的解决方案!!! - Rajinder
2
谢谢,这很有帮助。如果你的代码中有JAXBContext.newInstance选项,你也可以将其放在循环外面并保持为单例模式。此外,该错误似乎已经在jaxb-2.1.13中得到了修复(jira票:JAXB-564)。 - Thierry
谢谢,这解决了使用JAXB进行数据绑定的JAX-WS客户端的相同问题。 - Harry.Chen
1
为了方便搜索:JAXB-564 - Lonzak
需要把它放在哪个文件中? - Rajesh N

5
这里是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);

JAXB-564 Bug +修复方案可以在这里找到。受scorgar的已消失的博客启发。

受 http://www.scorgar.be/blog/lessons-learned-memory-leak-in-webservices-with-jaxb/ 的启发。 - Lonzak
1
是的,但它永久可在此处获取:https://web.archive.org/web/20171228110409/http://www.scorgar.be:80/blog/lessons-learned-memory-leak-in-webservices-with-jaxb - Lonzak

3

JAXBContext.newInstance()应该只用于创建一个上下文来反序列化您的类。否则,它将使用掉您的permgen或metaspace。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接