java 旧的 JaxB 和 JDK8 Metaspace OutOfMemory 问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33255578/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 21:25:50  来源:igfitidea点击:

Old JaxB and JDK8 Metaspace OutOfMemory Issue

javajaxbjava-8out-of-memoryjava-metro-framework

提问by The_Gentleman

We are working on a business application (1 million+ LOC) developed since 10+ years. While switching to JDK8 we get an issue with the metaspace of JDK8. This seems to be related to the JaxB-Version referenced in com.sun.xml.ws:webservices-rt:1.4 (Metro 1.4). Because of the intense linking in the application and legacy creation of classes/instances via JaxB it isn't simple to switch on the fly the old libraries.

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

Currently we are researching this issue. We created a sample programm that reproduces this behavior:

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

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 keeps the PermGenSpace clean. (Simulated with 16M PermGen) Memory of run with JDK7

JDK7 保持 PermGenSpace 干净。(用 16M PermGen 模拟) 用 JDK7 运行的内存

Using JDK8 the application runs slowly to the OOM exception. The VisualVM catches the exception and keeps the process running on the maximum of available Metaspace. Even here it gets stucked after quite a while running on max. (Simulated with 16M Metaspace) Memory of run with JDK8

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

Has anyone some ideas how to get the garbage collectors legacy behavior, so we don't run into those out of memory issues? Or do you have any other ideas how to deal with this issue?

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

Thanks.

谢谢。

edit1: Run parameters JDK7:

编辑1: 运行参数JDK7:

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

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

=> No heap dumps are created

=> 没有创建堆转储

Run parameters JDK8:

运行参数JDK8:

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

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

=> heap dumps are generated while running.

=> 运行时生成堆转储。

The Memory available of VisualVM does not show the real maximum metaspace value. If not limited the metaspace is constantly increasing untill memory is exceeded.

VisualVM 的可用内存没有显示真正的最大元空间值。如果不受限制,元空间会不断增加,直到超出内存。

edit 2:

编辑2:

I have tried all available garbage collectors for JDK8. They all have the same issue.

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

edit 3:

编辑3:

Solving by exchanging the libs is difficult in our real application because of heavy coupling between JAXB & several modules of our application. So a fix for the garbage collector behavior is needed for the short run. On the long run the propper fix is already planned.

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

回答by The_Gentleman

We solved our current issue untill able to fix all occurances in our application by using the following VM-parameter:

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

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

I hope this will help others with similar issues...

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

回答by Gary Barker

JAXBContext.newInstance() should be used once to create a context for your class to unmarshall. It will use up your permgen or metaspace otherwise.

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

回答by Lonzak

Here is the solution Gary is talking about, which is better than just setting a flag (since even the JAXB guyssuggest to make it singleton...)

这是加里正在谈论的解决方案,这比仅仅设置一个标志要好(因为即使是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);