JDK 8起永久代被元空间取代,元空间使用本地内存、受-XX:MaxMetaspaceSize控制,字符串常量池位于堆中,GC日志和jstat需关注MU/MC字段,元空间GC仅随Full GC触发且依赖ClassLoader回收。

永久代在JDK 8中已被移除
永久代(PermGen)是JDK 7及之前版本中用于存放类元数据、常量池、静态变量等的堆外内存区域。它被设计为固定大小,容易因加载过多类(如热部署场景)触发java.lang.OutOfMemoryError: PermGen space。JDK 8起,永久代被彻底移除,由元空间(Metaspace)替代——这不是简单的改名,而是架构级重构。
元空间使用本地内存而非JVM堆内存
元空间不再占用-Xmx控制的Java堆空间,而是直接分配在本地内存(Native Memory),由操作系统管理。这意味着:
-
-XX:MaxPermSize参数在JDK 8+已无效,设置会被忽略 - 类元数据的上限由
-XX:MaxMetaspaceSize控制(默认无上限,可能耗尽系统内存) - 元空间会随着类加载动态扩容,但卸载类后空间可被回收(依赖完整的GC和类卸载条件)
- 若频繁生成类(如大量使用
cglib、ASM或OSGi),仍可能触发java.lang.OutOfMemoryError: Metaspace
字符串常量池位置变化影响GC行为
JDK 7把字符串常量池从永久代移到了Java堆中;JDK 8延续该设计。这意味着:
-
String.intern()产生的对象现在位于堆内,受Young GC和Full GC管理 - 堆内存压力增大,尤其在大量调用
intern()且字符串不可复用时,可能加速老年代膨胀 - 永久代时代常见的“常量池撑爆PermGen”问题不复存在,但需关注堆内字符串对象生命周期
调试元空间需换用新JVM参数
排查元空间问题不能沿用永久代的老参数。关键可用选项包括:
立即学习“Java免费学习笔记(深入)”;
-
-XX:MetaspaceSize:初始元空间容量(触发首次Metaspace GC的阈值,默认约20MB) -
-XX:MaxMetaspaceSize:硬性上限,建议生产环境显式设置(如-XX:MaxMetaspaceSize=256m) -
-XX:+PrintGCDetails:GC日志中会出现Metaspace使用量(如Metaspace used 12345K, capacity 12456K, committed 12500K, reserved 1073741824K) -
jstat -gc输出中的MU(Metaspace Used)和MC(Metaspace Capacity)字段可实时监控
jstat -gc 12345 S0C S1C EC OC MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 0.0 0.0 262144.0 2097152.0 1048576.0 1024567.0 1048576.0 1024567.0 123 1.234 5 0.567 1.801
真正容易被忽略的是:元空间的GC不会单独触发,它只随Full GC发生(且仅当元空间不足时尝试回收无用类),而类卸载的前提是该类的ClassLoader已被回收——这要求整个类加载器树不可达,实践中很难满足,尤其在Web容器中。









