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内存使用情况 图像显示permgen使用超时,最后JVM终止

现在相同的代码针对Java 8(jdk1.8.0_40-ea)运行,并且正如预期的那样它继续扩展本机内存(Metaspace)但令人惊讶的是,1g的Metaspace它在OldGen中消耗了3g的堆(随着时间的推移几乎是Metaspace的3倍)

Java8内存使用情况 图像显示Metaspace使用超时和系统内存使用示例

这封来自Jon Masamitsu的电子邮件和这张JEP门票说

interned String和Class stats以及一些misc数据已移至Heap

当它将更多类加载到Metaspace中时,究竟是什么使堆增加?

运行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 

当它将更多类加载到Metaspace中时,究竟是什么使堆增加?

我的假设是,这是你的例子正在创建的“普通”垃圾。 我猜测:

  • javaassist代码创建常规堆对象。 它们大多是“大”的,并且会导致它们直接分配到OldGen堆中。 或其他原因导致。

    更新 – 看看@apangin的答案,我现在怀疑他们是从YoungGen堆中开始的,并且终身……)

  • 当在引擎盖下调用classLoader.defineClass ,它会从包含类文件的字节数组中创建元空间中的对象。

  • OldGen的用法仍然存在……因为还没有任何东西触发完整的GC。

如果你调整了你的例子,以便可以访问类,然后强制使用完整的GC,我希望(希望)看到OldHeap的使用率下降,表明它是“普通”垃圾而不是存储泄漏。