2014-10-19 62 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-EA),並如預期它不斷擴大天然存儲器(元空間),但令人驚訝的對1g Metaspace在OldGen中消耗了3g堆(almos元空間中的噸3×隨時間保持)

Java8 memory usage 圖像顯示元空間中使用的加班費和系統內存使用樣品

this email from Jon Masamitsuthis 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中 – 2014-10-19 22:53:39

3

究竟是什麼導致了堆的增加,因爲它將更多類加載到Metaspace中?

我的假設是,這是您的示例正在創建的「普通」垃圾。我猜測:

  • javaassist代碼創建常規堆對象。他們大多數是「大」,並導致他們被直接分配到OldGen堆。或者別的什麼原因導致。

    UPDATE - 看着@ apangin的答案,我現在懷疑他們在YoungGen堆開始了並終身教職...)

  • classLoader.defineClass在引擎蓋下調用,它創建的對象元數據空間來自包含類文件的字節數組。

  • OldGen用法仍然存在...因爲沒有任何東西觸發完整的GC。

如果您優化了你的例子,這樣的類人可到達,然後強制完整GC,我希望(希望)看OldHeap使用率下降,這表明它是「普通」的垃圾,而不是存儲泄漏。

+0

感謝斯蒂芬,這完全讓現在感覺,我覺得我沒有在Java 7的例子這種規模的分配是達到爲什麼我沒有看到它在Java 7 – 2014-10-19 22:53:15