java 8 后 outofmemoryerror: permgen space 变为 metaspace 是因永久代被移除,类元数据改由本地内存的元空间管理,涉及存储位置、回收机制和默认行为的全面重构。

为什么 Java 8 后 OutOfMemoryError: PermGen space 变成了 OutOfMemoryError: Metaspace
因为永久代(PermGen)被彻底移除了,类的元数据改由本地内存中的元空间(Metaspace)承载。这不是简单改名,而是存储位置、回收机制和默认行为的全面重构。
- 永久代是堆内一块受
-XX:MaxPermSize严格限制的连续区域;元空间则从 JVM 堆里“搬出去”,直接使用操作系统本地内存(native memory) - 永久代满时抛
OutOfMemoryError: PermGen space,且 GC 会强制连带老年代一起回收;元空间满时抛OutOfMemoryError: Metaspace,GC 只清理无用类加载器关联的元数据,不触发老年代回收 - JDK 7 开始已逐步把字符串常量池、静态变量等挪到堆中,JDK 8 彻底把类元信息(如 Class 对象结构、方法字节码签名、注解信息)全交由元空间管理
-XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 到底怎么配
这两个参数控制元空间的初始容量和上限,但它们的行为和永久代参数有本质区别:不设 -XX:MaxMetaspaceSize 时,元空间理论上可涨到系统剩余本地内存耗尽为止——这既是灵活性,也是危险源。
-
-XX:MetaspaceSize是触发首次元空间 GC 的阈值(类似年轻代的-XX:InitialHeapSize),默认约 20–24MB;超过它且发生类卸载需求时,才会启动元空间回收 -
-XX:MaxMetaspaceSize必须显式设置才能防止失控增长;生产环境强烈建议设为固定值(例如-XX:MaxMetaspaceSize=512m),否则容器环境下容易因 RSS 内存超限被 kill - 注意:
-XX:PermSize和-XX:MaxPermSize在 JDK 8+ 已完全失效,若保留会触发 JVM 启动警告甚至报错
类加载爆炸时,元空间比永久代更容易 OOM 吗
不一定更易 OOM,但更难预测——因为它的增长不再受限于一个预设的“堆内小房间”,而直连操作系统内存。尤其在热部署、OSGi、大量动态代理(如 Spring AOP)、或自定义类加载器未正确释放的场景下,元空间可能悄无声息地吃掉几 GB 本地内存。
- 典型症状:JVM 进程 RSS 内存持续上涨,但堆内存(
jstat -gc显示的OU/OU)稳定;jstat -gc中MU(Metaspace Used)不断上升,MC(Metaspace Capacity)同步扩大 - 排查命令:
jcmd <pid> VM.native_memory summary scale=MB</pid>查看元空间实际占用;jmap -clstats <pid></pid>统计加载的类加载器数量及类数 - 关键点:元空间不会自动收缩。即使大量类被卸载,已分配的本地内存也不会立刻还给 OS,需靠 Full GC 触发回收(且仅当类加载器本身可被回收时才有效)
升级 JDK 8+ 后,原来调优永久代的参数要怎么改
全部删掉或替换。永久代相关参数在 JDK 8 中已被废弃,继续使用不仅无效,还会干扰 JVM 启动流程。
立即学习“Java免费学习笔记(深入)”;
- 删掉:
-XX:PermSize、-XX:MaxPermSize、-XX:+UseConcMarkSweepGC(CMS 不支持元空间并发回收,JDK 9 起彻底移除) - 替换为:
-XX:MetaspaceSize=128m+-XX:MaxMetaspaceSize=512m(根据应用类数量基线调整,Spring Boot 应用常见设为 256–512m) - 补充建议:
-XX:MinMetaspaceFreeRatio=40和-XX:MaxMetaspaceFreeRatio=70可减少因碎片导致的频繁扩容,但仅在高频率类加载/卸载场景下有意义
元空间不是“永久代换个名字”,它是把类元数据管理从堆内存的强约束中解放出来,代价是把内存风险从 JVM 内部转移到了操作系统层面。真正麻烦的从来不是参数怎么写,而是类加载器生命周期没理清——只要一个泄漏的 ClassLoader 活着,它加载的所有类元数据就永远钉在元空间里。










