Java 8中堆内存与Metaspace为何同步增长?
为什么动态创建类时Metaspace和堆内存同时增长?
这是个非常贴近实践的问题,我来帮你拆解背后的原因:
核心原因拆解
1. ClassPool的堆内缓存
你用的ClassPool是CGLib的核心类,它本身是存储在堆内存中的,并且默认会缓存所有通过它创建的类元数据相关对象。每调用一次makeClass,ClassPool都会把生成的类信息(包括内部的类结构、字节码引用等)保留在堆里的缓存中。随着你创建10万个类,这份缓存的体积会持续膨胀,直接推高堆内存占用。
2. java.lang.Class对象存于堆中
虽然类的元数据(比如方法字节码、字段描述符、常量池等)确实存在Metaspace里,但**java.lang.Class这个实例本身是在堆内存分配的**。你循环创建10万个Class对象,每个对象都要占用堆空间,累积起来就是一笔不小的内存开销,这是堆内存上升的关键原因之一。
3. 类生成过程中的临时对象
在CGLib动态生成类的过程中,会产生大量临时对象:比如拼接类名的字符串、存储字节码的字节数组、编译器的辅助实例等。这些对象都在堆中分配,即便垃圾回收会清理一部分,但短时间内高频创建的情况下,堆内存的占用会快速上升。
验证建议
你可以修改测试代码来验证上述结论:
@Test public void metaspaceTest() throws CannotCompileException, InterruptedException { ClassPool cp = ClassPool.getDefault(); System.out.println("started"); for (int i = 0; i <= 100000; i++) { String className = "br.com.test.GeneratedClass" + i; Class c = cp.makeClass(className).toClass(); // 创建后移除ClassPool中的缓存 cp.remove(className); Thread.sleep(1); if (i % 10000 == 0) { System.out.println(i); // 手动触发GC(仅用于测试) System.gc(); } } System.out.println("finished"); }
修改后再观察内存变化,你会发现堆内存的增长速度会明显放缓——这就说明之前的堆内存上升确实和ClassPool的缓存以及Class对象的堆积有关。
内存变化截图说明
- 第一张截图:Metaspace占用随类创建数量逐步攀升,同时堆内存的used区域也同步呈现持续增长的趋势,两者的变化曲线基本平行。
- 第二张截图:可以看到堆内存的占用峰值接近配置的128MB上限,和Metaspace的占用增长保持同步。
内容的提问来源于stack exchange,提问作者Andre
相关产品推荐
相关产品推荐

