useconcmarksweepgc outofmemoryerror java jvm java-8 jvm-hotspot metaspace

useconcmarksweepgc - java.lang.outofmemoryerror: metaspace



Java8 metaspace y uso del montón (2)

Tengo este código para generar clase dinámicamente y cargarlo.

import javassist.CannotCompileException; import javassist.ClassPool; public class PermGenLeak { private static final String PACKAGE_NAME = "com.jigarjoshi.permgenleak."; public static void main(String[] args) throws CannotCompileException, InterruptedException { for (int i = 0; i < Integer.MAX_VALUE; i++) { ClassPool pool = ClassPool.getDefault(); pool.makeClass(PACKAGE_NAME + i).toClass(); Thread.sleep(3); } } }

Lancé esta clase contra Java 7 (jdk1.7.0_60) y, como se esperaba, llenó PermGenSpace y el montón no se utilizó La imagen muestra las horas extraordinarias de uso de Permgen y al final se finalizó la JVM.

Ahora, el mismo código se ejecutó contra Java 8 (jdk1.8.0_40-ea) y, como se esperaba, siguió expandiendo la memoria nativa (Metaspace), pero sorprendentemente para 1 g de Metaspace consumió 3 g de Heap en OldGen (casi 3x de Metaspace se mantuvo con el tiempo)

La imagen muestra las horas extra de uso de Metaspace y el ejemplo de uso de la memoria del sistema.

Este correo electrónico de Jon Masamitsu y este boleto de JEP dice

las estadísticas internas de String y Class y algunos datos misceláneos se han movido a Heap

¿Qué hace exactamente este aumento en el montón a medida que carga más clases en Metaspace?


¿Qué hace exactamente este aumento en el montón a medida que carga más clases en Metaspace?

Mi hipótesis es que esto es basura "común" que se crea con su ejemplo. Supongo que

  • El código javaassist crea objetos de pila regulares. En su mayoría son "grandes" y eso hace que se asignen directamente al montón OldGen. O algo más causa eso.

    ( ACTUALIZACIÓN : mirando la respuesta de @apangin, ahora sospecho que comenzaron en el montón de YoungGen y fueron titulares ...)

  • Cuando se llama a classLoader.defineClass bajo el capó, crea objetos en el metaspacio desde la matriz de bytes que contiene el archivo de clase.

  • El uso de OldGen permanece ... porque aún nada ha activado un GC completo.

Si ajustara su ejemplo para que las clases estuvieran disponibles y luego forzara un GC completo, esperaría (espero) ver que el uso de OldHeap disminuya, lo que indica que se trata de basura "normal" en lugar de una fuga de almacenamiento.


Ejecute jmap -histo PID para ver qué objetos consumen el espacio de jmap -histo PID .
Cuando ejecuté tu ejemplo, vi el montón lleno de objetos auxiliares de Javassist:

num #instances #bytes class name ---------------------------------------------- 1: 592309 312739152 [Ljavassist.bytecode.ConstInfo; 2: 6515673 208501536 java.util.HashMap$Node 3: 2964403 169188824 [C 4: 1777622 102165184 [Ljava.lang.Object; 5: 4146200 99508800 javassist.bytecode.Utf8Info 6: 3553889 85293336 java.util.ArrayList 7: 2964371 71144904 java.lang.String 8: 593075 56944008 java.lang.Class 9: 592332 47388032 [Ljava.util.HashMap$Node; 10: 592309 37907776 javassist.bytecode.ClassFile 11: 592308 37907712 javassist.CtNewClass 12: 1185118 28555808 [B 13: 592342 28432416 java.util.HashMap 14: 1184624 28430976 javassist.bytecode.ClassInfo 15: 592309 28430832 [[Ljavassist.bytecode.ConstInfo; 16: 592322 23692880 javassist.bytecode.MethodInfo 17: 592315 23692600 javassist.bytecode.CodeAttribute 18: 592434 18957888 java.util.Hashtable$Entry 19: 592309 18953888 javassist.bytecode.ConstPool 20: 592308 18953856 java.lang.ref.WeakReference 21: 592318 14215632 javassist.bytecode.MethodrefInfo 22: 592318 14215632 javassist.bytecode.NameAndTypeInfo 23: 592315 14215560 javassist.bytecode.ExceptionTable 24: 592309 14215416 javassist.bytecode.LongVector 25: 592309 14215416 javassist.bytecode.SourceFileAttribute 26: 592507 9487584 [I 27: 8 6292528 [Ljava.util.Hashtable$Entry; 28: 212 18656 java.lang.reflect.Method 29: 407 13024 java.util.concurrent.ConcurrentHashMap$Node 30: 124 8928 java.lang.reflect.Field