Reproduire un débordement de la PermGen ou du Metaspace

Pour comprendre comment simuler un dépassement de mémoire dans la PermGen ou le Metaspace, commençons par examiner ce qui y est stocké : des informations de types, des constantes, des variables statiques, et le cache de code compilé à la volée.

Les constantes, comme celles obtenues via String.intern(), étaient gérées différemment selon les versions de JDK. À partir de JDK7, la table des constantes de chaînes et les variables statiques ont été déplacées vers le tas, laissant principalement les informations de types et le cache de code dans le Metaspace.

Ainsi, pour provoquer un dépassement, il suffit de générer dynamiquement un nombre illimité d'informations de types en exécution. La bibliothèque CGLib permet de créer des classes dynamiques via des proxys.

Par défaut, CGLib active un cache pour réutiliser les classes générées, ce qui évite les fuites mémoire. Pour simuler le débordement, il faut désactiver ce cache et itérer la création de proxys.

public class ServiceCible { public void methodeReelle() { System.out.println("Exécution de la méthode réelle"); }

public static void main(String[] args) {
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/chemin/vers/repertoire");
    GenerateurProxy generateur = new GenerateurProxy();
    generateur.setUtiliserCache(false);
    generateur.setClasseParent(ServiceCible.class);
    generateur.setCallback(new MonIntercepteurService());

    ServiceCible instance = (ServiceCible) generateur.creer();
    for (int i = 0; i < 1000; i++) {
        System.out.println("Itération: " + i);
        instance = (ServiceCible) generateur.creer();
    }
    instance.methodeReelle();
}

}


</div><div>Classe d'interception : ```

public class MonIntercepteurService implements MethodInterceptor {
    @Override
    public Object intercepter(Object objet, Method methode, Object[] arguments, MethodProxy proxy) throws Throwable {
        System.out.println("Avant l'exécution");
        Object resultat = proxy.invokeSuper(objet, arguments);
        System.out.println("Après l'exécution");
        return resultat;
    }
}

... 523 524 Exception in thread "main" org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345) ... Caused by: java.lang.OutOfMemoryError: Metaspace at java.lang.ClassLoader.defineClass1(Native Method) ...


</div>Cette erreur confirme le dépassement du Metaspace dû à la génération continue de clases sans mise en cache.

Étiquettes: JVM Metaspace CGLib Java JDK8

Publié le 11 juin à 03h51