为什么在 Java 8 中堆内存与元空间一起上升?
Why is heap memory going up together with metaspace in Java 8?
我正在做一个小测试来了解元空间内存(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");
}
见下图:
您的 class 池使用堆内存。它有 hashtables 和列表以及其他东西。它还使用 java 使用堆内存的反射代码。未被 gc'ed 的堆内存可能是 class 池中的所有数据结构、一对哈希 tables、链表、数组列表、池等......例如,每个 class 您创建的存储在 class 池的散列 table 中。那是一个 100,000 个元素的哈希 table.
其次,如果您正在创建的 classes 中有任何静态初始值设定项,那将使用堆内存。静态初始化器包括静态字段初始化和静态块代码。
我特别研究了您的代码 ClassPool#makeClass
。我注意到有几点导致堆 space 随着 metaspace 的增加而增加。
- 它
cache
class 由哈希表中的 makeClass 方法创建的 es
protected Hashtable classes;
因此,对于 classes 的百万分之一,它具有其中每一个的条目。因此,堆 Space 也增加了,它不是 GC,因为您的 for 循环仍在使用哈希表引用并不断更新,因此不符合 gc 的条件。
CtNewClass
创建 class 的新实例,它的构造函数定义如下:
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;
}
在上面的代码中,this.classfile = new ClassFile(isInterface, name, superName);
行实际上为每个 class 创建了新的 ConstPool 实例,即为每个实例创建了新的 HashMap 实例,这些实例在堆 space.
上保留了内存
HashMap classes; //from ConstPool class
HashMap strings; //from ConstPool class
此外,这将创建两个新的 ArrayList。观察上面构造函数中的 this.fields = new ArrayList();
和 this.methods = new ArrayList();
语句。另外,一个新的链表 this.attributes = new LinkedList();
.
因此,结论是 ClassPool 的缓存管理占用了大量的堆 space。然后每个 class 都有自己的一组集合来管理属性、常量等
希望对您有所帮助!
我正在做一个小测试来了解元空间内存(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");
}
见下图:
您的 class 池使用堆内存。它有 hashtables 和列表以及其他东西。它还使用 java 使用堆内存的反射代码。未被 gc'ed 的堆内存可能是 class 池中的所有数据结构、一对哈希 tables、链表、数组列表、池等......例如,每个 class 您创建的存储在 class 池的散列 table 中。那是一个 100,000 个元素的哈希 table.
其次,如果您正在创建的 classes 中有任何静态初始值设定项,那将使用堆内存。静态初始化器包括静态字段初始化和静态块代码。
我特别研究了您的代码 ClassPool#makeClass
。我注意到有几点导致堆 space 随着 metaspace 的增加而增加。
- 它
cache
class 由哈希表中的 makeClass 方法创建的 es
protected Hashtable classes;
因此,对于 classes 的百万分之一,它具有其中每一个的条目。因此,堆 Space 也增加了,它不是 GC,因为您的 for 循环仍在使用哈希表引用并不断更新,因此不符合 gc 的条件。
CtNewClass
创建 class 的新实例,它的构造函数定义如下: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; }
在上面的代码中,this.classfile = new ClassFile(isInterface, name, superName);
行实际上为每个 class 创建了新的 ConstPool 实例,即为每个实例创建了新的 HashMap 实例,这些实例在堆 space.
HashMap classes; //from ConstPool class
HashMap strings; //from ConstPool class
此外,这将创建两个新的 ArrayList。观察上面构造函数中的 this.fields = new ArrayList();
和 this.methods = new ArrayList();
语句。另外,一个新的链表 this.attributes = new LinkedList();
.
因此,结论是 ClassPool 的缓存管理占用了大量的堆 space。然后每个 class 都有自己的一组集合来管理属性、常量等
希望对您有所帮助!