Java8 元空间和堆内存使用情况

16

我有这段代码动态生成并加载类

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

    }
}

我针对Java7 (jdk1.7.0_60)启动了这个类,如预期的那样填满了PermGenSpace,而堆保持未使用状态。 Java 7 memory usage 图像显示随时间变化的permgen使用情况,最后JVM被终止。

现在同样的代码运行在Java8 (jdk1.8.0_40-ea)上,如预期的那样不断扩展本地内存(Metaspace),但令人惊讶的是,为了1g的Metaspace,它在OldGen中消耗了3g的Heap(几乎是维持Metaspace的3倍)。

Java8 memory usage 图像显示随时间变化的Metaspace使用情况和系统内存使用情况示例。

Jon Masamitsu的这封电子邮件这个JEP票据说:

interned String和Class stats以及一些其他数据已经被移动到Heap中。

是什么导致这个堆增加,当它把更多的类加载到Metaspace中?

2个回答

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

谢谢Andrei,我应该只是执行这个,我想我在Java 7的例子中没有达到这个规模,所以我没有在Java 7中看到它。 - jmj

3
究竟是什么造成了随着更多类加载到Metaspace中堆的增加?我的假设是这是由你的示例创建的“普通”垃圾。我推测: - javaassist代码创建常规堆对象。它们大多是“大”的,这会导致它们直接分配到OldGen堆中。或者其他原因导致了这种情况。 (更新 - 查看@apangin的答案后,我现在怀疑它们最初在YoungGen堆中,并被准备...) - 在幕后调用classLoader.defineClass时,它从包含类文件的字节数组中在metaspace中创建对象。 - OldGen的使用仍然存在...因为尚未触发全GC。
如果你调整示例以使类可达,并强制进行全GC,我期望(希望)看到OldHeap使用下降,表明它是“普通”的垃圾而不是存储泄漏。

谢谢Stephen,现在完全明白了,我想我在Java 7的示例中没有达到这种分配规模,所以我没有看到它在Java 7中。 - jmj
你能否请提供包含示例代码的解决方案? - Grayden Hormes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接