2014-10-19 2 views
15

У меня есть этот код, чтобы сгенерировать класс динамически и загрузить егоJava8 Метапространство и куча использования

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); 
     } 

    } 
} 

Я начал этот класс против Java 7 (jdk1.7.0_60) и, как ожидается, она заполнена PermGenSpace и куча осталась неиспользованной Java 7 memory usage изображение показывает PermGen сверхурочно использования и в конце JVM прекращали

Теперь тот же самый код бежал против Java 8 (jdk1.8.0_40-ЭА) и, как ожидается, она продолжала расширяться родную память (Метапространстве), но удивительно для 1g из Метаспасе он потреблял 3g кучи в OldGen (almos т 3x из Метапространства поддерживается в течение долгого времени)

Java8 memory usage изображения показывает использование Метапространства сверхурочное и система выборка использования памяти

this email from Jon Masamitsu и this JEP ticket говорят

интернировано String и класс статистики, а некоторые различ данные имеют были перемещены в кучу

что именно делает это увеличение кучи, когда он загружает больше классов в Metaspace?

ответ

10

Запустить jmap -histo PID, чтобы увидеть, какие объекты потребляют пространство кучи.
Когда я запустил свой пример, который я видел кучу полного 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 
+0

Спасибо Андрею, я должен был просто выполнить это, я думаю, что я не достиг такого масштаба в примере Java 7, поэтому я не видел его в Java 7 –

3

Что именно делает это увеличение кучи, поскольку оно загружает больше классов в Metaspace?

Моя гипотеза заключается в том, что это «обычный» мусор, который создается вашим примером. Я предполагаю, что:

  • Код javaassist создает обычные объекты кучи. Они в основном «большие», и это заставляет их выделяться непосредственно в кучу OldGen. Или что-то еще вызывает это.

    (UPDATE - глядя на @ apangin Ответит, теперь я подозреваю, что они начали в куче YoungGen и были штатные ...)

  • Когда classLoader.defineClass вызываются под капотом, он создает объекты metaspace из массива байтов, содержащего файл класса.

  • Использование OldGen остается ... потому что ничто не вызвало полный GC еще.

Если вы подправили свой пример так, чтобы классы были достижимы, а затем заставили полный GC, я хотел бы ожидать (надеюсь), чтобы увидеть использование OldHeap падать, указывая, что это «обычный» мусор, а не утечка хранилища.

+0

Благодаря Стивен, это полностью имеет смысл теперь, я думаю, что я не достиг в этом масштабе размещения в Java 7, например, что является почему я не видел его в Java 7 –