方法区不是堆内存的一部分,它专门存放类的元数据;JDK 8起由Metaspace替代永久代,使用本地内存,默认无上限,仅受系统内存约束,GC回收更懒,需关注类加载器泄漏导致的Metaspace持续增长。

方法区不是堆内存的一部分,它专门存放类的元数据,比如类结构、常量池、静态变量、即时编译后的代码等。在 JDK 8 及之后,方法区的物理实现已从永久代(PermGen)彻底移除,改由 Metaspace 承担——这是关键判断。
Metaspace 和永久代的根本区别
永久代是堆内存中一块固定大小的区域,受 -XX:MaxPermSize 限制,容易因加载过多类而触发 java.lang.OutOfMemoryError: PermGen space;Metaspace 则直接使用本地内存(native memory),默认无上限(仅受系统内存约束),通过 -XX:MaxMetaspaceSize 手动设限才生效。
- 永久代还混存了字符串常量池(
String.intern()的对象),JDK 7 起已移到堆中;Metaspace 不管字符串,只管类元数据 - GC 对 Metaspace 的回收更“懒”:只有在 full GC 或元空间不足时,才会卸载无用类并释放空间
-
jstat -gc输出中的M(Metaspace)和MC(Metaspace Capacity)列,替代了旧版的P(Perm)列
哪些操作会真实增长 Metaspace 使用量
不是所有“定义类”的行为都会立刻占用 Metaspace;只有类被成功加载并解析后,其元数据才写入。常见真实增长场景包括:
- 动态生成类(如 CGLIB、ASM、Lombok 编译期生成的代理类)
- OSGi 框架中频繁 install/uninstall bundle(每个 bundle 含独立类加载器和类元数据)
- 热部署容器(如 Tomcat 重启 WebApp 但未清理旧类加载器,导致类元数据泄漏)
- 大量使用
Class.forName("xxx")+ 自定义ClassLoader且不重用
注意:new Object() 或普通对象实例化完全不影响 Metaspace,它们分配在堆上。
立即学习“Java免费学习笔记(深入)”;
排查 Metaspace 泄漏的实用命令
当出现 java.lang.OutOfMemoryError: Metaspace,优先确认是否真泄漏,而非简单调大 -XX:MaxMetaspaceSize:
jstat -gcmetacapacity
看 MC(当前容量)、MU(已使用)和 CCSC(压缩类空间容量)是否持续上涨;再配合:
jmap -clstats
输出每个类加载器加载的类数量——若某个 ClassLoader 实例反复创建且类数飙升,基本可定位泄漏源头。Java 11+ 还可用:
jcmdVM.native_memory summary scale=MB
检查 Internal 区域中 Metaspace 的实际占用。
Metaspace 看似“自动管理”,但类加载器生命周期失控时,它的增长是静默且不可逆的——只要类加载器没被回收,它加载的所有类元数据就一直钉在 native 内存里,GC 也清不掉。










