【问题标题】:Java8 metaspace & heap usageJava 8 元空间和堆使用
【发布时间】:2014-10-19 21:12:47
【问题描述】:

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

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 并且堆仍未使用 图像显示 permgen 使用超时,最后 JVM 被终止

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

图片显示超空间使用超时和系统内存使用示例

this email from Jon Masamitsuthis JEP ticket

interned String 和 Class stats 和一些杂项数据已移至 Heap

在将更多类加载到 Metaspace 时,究竟是什么导致了堆的增加?

【问题讨论】:

    标签: java jvm java-8 jvm-hotspot metaspace


    【解决方案1】:

    运行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 中没有看到它
    【解决方案2】:

    在将更多类加载到 Metaspace 时,究竟是什么导致了堆的增加?

    我的假设是,这是由您的示例创建的“普通”垃圾。我猜:

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

      更新 - 看看@apangin 的回答,我现在怀疑他们是在 YoungGen 堆中开始的,并且已经获得终身教职......)

    • classLoader.defineClass 在后台被调用时,它会根据包含类文件的字节数组在元空间中创建对象。

    • OldGen 的使用仍然存在......因为尚未触发完整的 GC。

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

    【讨论】:

    • 感谢 Stephen,现在完全说得通了,我想我在 Java 7 示例中没有达到这种分配规模,这就是为什么我在 Java 7 中没有看到它
    • 能否请您提供代码来演示您的解决方案
    猜你喜欢
    • 1970-01-01
    • 2015-07-07
    • 2021-09-09
    • 2014-03-05
    • 2014-11-16
    • 1970-01-01
    • 2011-10-27
    • 2022-06-16
    • 1970-01-01
    相关资源
    最近更新 更多