Старый выпуск 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.

Другие вопросы по тегам