Подтвердить что ты не робот

Старый JaxB и JDK8 Metaspace OutOfMemory Issue

Мы работаем над бизнес-приложением (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, не показывает реального максимального значения метапоса. Если не ограничено, метапас будет постоянно увеличиваться до тех пор, пока память не будет превышена.

edit 2:

Я попробовал все доступные сборщики мусора для JDK8. У всех у них такая же проблема.

edit 3:

Решения об обмене библиотеками сложны в нашем реальном приложении из-за сильной связи между JAXB и несколькими модулями нашего приложения. Поэтому для краткосрочного запуска требуется исправление поведения сборщика мусора. В долгосрочной перспективе исправление исправления уже запланировано.

4b9b3361

Ответ 1

Мы решили нашу текущую проблему, пока не можем исправить все события в нашем приложении, используя следующий параметр VM:

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

Я надеюсь, что это поможет другим с подобными проблемами...

Ответ 2

JAXBContext.newInstance() следует использовать один раз, чтобы создать контекст для вашего класса для отмены маркера. В противном случае он будет использовать ваш permgen или metaspace.

Ответ 3

Вот решение, о котором говорит Гэри, которое лучше, чем просто установка флага (поскольку даже ребята из 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);