web-dev-qa-db-fra.com

Ancien problème Java et JDK 8 Metaspace OutOfMemory

Nous travaillons sur une application métier (1 million + LOC) développée depuis plus de 10 ans. En passant à JDK8, nous rencontrons un problème avec le méta-espace de JDK8. Cela semble être lié à la version JaxB référencée dans com.Sun.xml.ws:webservices-rt:1.4 (Metro 1.4). En raison de la liaison intense qui existe dans l'application et de la création héritée de classes/instances via JaxB, il n'est pas simple de basculer à la volée des anciennes bibliothèques.

Nous étudions actuellement ce problème. Nous avons créé un exemple de programme qui reproduit ce problème:

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 maintient le PermGenSpace propre. (Simulé avec 16M de PermGen) Mémoire de course avec JDK7

Avec JDK8, l'application s'exécute lentement à l'exception de MOO. Le VisualVM intercepte l'exception et maintient le processus en cours d'exécution au maximum du métaspace disponible. Même ici, il reste bloqué après un certain temps de fonctionnement maximum. (Simulé avec 16M métaspace) Mémoire d’exécution avec JDK8

Quelqu'un a-t-il des idées sur la manière d'obtenir le comportement hérité des éboueurs afin de ne pas nous heurter à des problèmes de mémoire insuffisante? Ou avez-vous d'autres idées sur la façon de traiter ce problème?

Merci.

edit1: Paramètres d'exécution JDK7:

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

=> Aucune sauvegarde de tas n'est créée

Paramètres d'exécution JDK8:

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

=> des vidages de tas sont générés lors de l'exécution.

La mémoire disponible de VisualVM n'affiche pas la valeur maximale réelle du métaspace. S'il n'est pas limité, le métaspace augmente constamment jusqu'à ce que la mémoire soit dépassée.

éditer 2:

J'ai essayé tous les éboueurs disponibles pour JDK8. Ils ont tous le même problème. 

éditer 3:

Résoudre par échange de bibliothèques est difficile dans notre application réelle en raison du couplage important entre JAXB et plusieurs modules de notre application. Un correctif du comportement du ramasse-miettes est donc nécessaire à court terme. À long terme, le correctif approprié est déjà prévu.

21
The_Gentleman

Nous avons résolu notre problème actuel jusqu'à ce que nous puissions résoudre toutes les occurrences de votre application à l'aide du paramètre de machine virtuelle suivant:

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

J'espère que cela aidera d'autres personnes ayant des problèmes similaires ...

26
The_Gentleman

JAXBContext.newInstance () doit être utilisé une fois pour créer un contexte pour votre classe à unmarshall. Sinon, votre permgen ou votre métaspace sera utilisé.

3
Gary Barker

Voici la solution dont Gary parle, ce qui est mieux que de simplement placer un drapeau (puisque même les gars de JAXB suggèrent de le rendre singleton ...) 

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);
0
Lonzak