2014-10-19 61 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