我们正在开发自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之间的耦合很大。我们应用的几个模块。因此,短期内需要修复垃圾收集器行为。从长远来看,已经计划了适当的修复。
答案 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);