Старый выпуск JaxB и JDK8 Metaspace OutOfMemory
Мы работаем над бизнес-приложением (1 миллион + LOC), разработанным более 10 лет назад. При переключении на JDK8 у нас возникает проблема с метапространством JDK8. Похоже, это связано с версией JaxB, на которую ссылается com.sun.xml.ws:webservices-rt:1.4 (Metro 1.4). Из-за интенсивного связывания в приложении и унаследованного создания классов / экземпляров через 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
У кого-нибудь есть идеи, как настроить устаревшее поведение сборщиков мусора, чтобы мы не сталкивались с проблемами нехватки памяти? Или у вас есть другие идеи, как решить эту проблему?
Благодарю.
edit1:Запустить параметры 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 и несколькими модулями нашего приложения. Поэтому для краткосрочного запуска необходимо исправить поведение сборщика мусора. В долгосрочной перспективе исправление проппера уже запланировано.
2 ответа
Мы решили нашу текущую проблему, пока не смогли исправить все в нашем приложении, используя следующий VM-параметр:
-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true
Я надеюсь, что это поможет другим с похожими проблемами...
Вот решение, о котором говорит Гэри, которое лучше, чем просто установка флага (поскольку даже парни из 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);
JAXBContext.newInstance() следует использовать один раз, чтобы создать контекст для демаршаллинга вашего класса. В противном случае он израсходует ваш permgen или metaspace.