【问题标题】:Old JaxB and JDK8 Metaspace OutOfMemory Issue旧的 JaxB 和 JDK 8 元空间内存不足问题
【发布时间】:2015-10-21 09:28:07
【问题描述】:

我们正在开发一个 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 捕获异常并保持进程在最大可用元空间上运行。即使在这里,在最大运行一段时间后它也会卡住。 (模拟16M元空间) Memory of run with 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 的可用内存不显示真正的最大元空间值。如果没有限制,元空间会不断增加,直到超出内存。

编辑 2:

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

编辑 3:

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

【问题讨论】:

  • “VisualVM 捕获异常并保持进程运行”——我从来不知道 VisualVM 可以做到这一点。除此之外,您的屏幕截图显示,在您的 Java8 运行中,元空间的限制为 1GB,但仍保持在 16MB,这与运行 OOME 相去甚远。元空间似乎没有被收集,但似乎还有一个简单而明显的原因:在 Java 8 设置中,它保留类,因为计数器始终保持在 ~4300 个类,而在 Java 7 运行中,已经统计了多达 43,000 个类。所以在那里,类似乎不断地重新生成......
  • OOM 在运行时显示在 Eclipse 调试器中。任务受运行配置上的参数限制为 16MB 元空间。这不适用于 VisualVM 实例。所以这表示还剩下其他数量的内存。如果我让应用程序在没有 VisualVM 的情况下运行,OOM 会在 JDK8 进程达到 16MB 元空间的同时停止进程。当使用例如webservices-rt 1.6.1 或删除它并使用 JDK8 JaxB 实现,示例程序保持恒定数量的 12.5MB 元空间。如果不限制 JDK8 内存,它将增加直到全部使用。
  • 我很困惑。屏幕截图显示了什么?您是在分析您的应用程序还是 VisualVM?
  • 图片展示了示例程序的运行分析。我在原文章中添加了运行参数。似乎 VisualVM 没有显示正确的 MaxMetaspace 值。 java.net/jira/browse/VISUALVM-609
  • 内存泄漏仅在使用 Java 8 时发生,在 Java 7 的情况下,GC 会毫无问题地清理未使用的类。正如问题中所述,从长远来看,切换 JaxB 版本对我们来说是可行的。

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


【解决方案1】:

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

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

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

【讨论】:

  • 当我们将应用程序从 JDK7 切换到 JDK8 时,我们也面临与 OOME 相关的类似问题,这个参数解决了我们的问题。感谢您发布解决方案!!!
  • 谢谢,有帮助。您还可以选择将 JAXBContext.newInstance 放在循环之外(如果它在您的代码中)并将其保留为单例。此外,该错误似乎已通过 jaxb-2.1.13(jira 票证:JAXB-564)修复
  • 谢谢,这解决了与使用 JAXB 进行数据绑定的 JAX-WS 客户端相同的问题。
  • 为了方便搜索:JAXB-564
  • 需要放在哪个文件中?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    可以在 here 找到 JAXB-564 错误 + 修复。灵感来自 scorgar 早已不复存在的博客

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-04
    • 1970-01-01
    • 2019-07-04
    • 2017-02-24
    • 1970-01-01
    • 2014-06-12
    • 2011-10-08
    相关资源
    最近更新 更多