为什么在Java 8中堆内存与元空间一起上升?

2022-09-04 23:08:21

我正在做一个小测试,以了解元空间内存(Java 8及更高版本)是如何工作的。当我动态创建100,000个类时,元空间内存正在增长(显然),但堆内存也在增加。有人可以向我解释为什么会发生这种情况吗?

PS:我正在使用128 MB的堆和128 MB的元空间运行测试。

@Test
public void metaspaceTest() throws CannotCompileException, InterruptedException {

ClassPool cp = ClassPool.getDefault();
System.out.println("started");

for (int i = 0; i <= 100000; i++) {
    Class c = cp.makeClass("br.com.test.GeneratedClass" + i).toClass();
    Thread.sleep(1);
    if (i % 10000 == 0) {
    System.out.println(i);
    }
}

System.out.println("finished");
}

请参阅下图:

enter image description here

enter image description here


答案 1

类池使用堆内存。它有哈希表和列表以及其他东西。它还使用使用堆内存的java反射代码。未gc'ed的堆内存可能是类池中的所有数据结构,几个哈希表,链接列表,数组列表,池等...例如,您创建的每个类都由类池存储在哈希表中。这是一个 100,000 元素哈希表。

其次,如果您正在创建的类中存在任何静态初始值设定项,这将使用堆内存。静态初始值设定项包括静态字段初始化和静态块代码。


答案 2

我特别研究了你的代码。我注意到有几个点会导致堆空间随着元空间的增加而增加。ClassPool#makeClass

  1. 它是在哈希表中由方法 makeClass 创建的类cache

受保护的哈希表类;

因此,对于百万分之一的班级,它都有每个班级的条目。因此,堆空间也增加了,它不是GC,因为哈希表引用仍然被您的for循环使用并不断更新,因此不符合gc的条件。

  1. CtNewClass创建类的新实例,并且它具有如下所示的构造函数定义:

      CtNewClass(String name, ClassPool cp, boolean isInterface, CtClass superclass) {
        super(name, cp);
        this.wasChanged = true;
        String superName;
        if (!isInterface && superclass != null) {
            superName = superclass.getName();
        } else {
            superName = null;
        }
    
        this.classfile = new ClassFile(isInterface, name, superName);
        if (isInterface && superclass != null) {
            this.classfile.setInterfaces(new String[]{superclass.getName()});
        }
    
        this.setModifiers(Modifier.setPublic(this.getModifiers()));
        this.hasConstructor = isInterface;
    }
    

在上面的代码中,行实际上为每个类创建新的ConstPool实例,即每个实例的新HashMap实例,这些实例在堆空间上保留内存。this.classfile = new ClassFile(isInterface, name, superName);

哈希映射类;从康斯特普尔类

哈希映射字符串;从康斯特普尔类

此外,这将创建两个新的 ArrayList。在上面的构造函数中观察和语句。此外,一个新的链表。this.fields = new ArrayList();this.methods = new ArrayList();this.attributes = new LinkedList();

因此,结论是ClassPool具有其缓存管理,它占用了大量的堆空间。然后,每个类都有自己的一组集合来管理属性、常量等。

希望它有帮助!


推荐