java 8前permgen溢出源于类元数据超限,非内存不足;jdk 8+改用自动扩容的metaspace,需移除-xx:maxpermsize并配置-xx:metaspacesize与-xx:maxmetaspacesize。

Java 8 以前持久代(PermGen)溢出的典型表现
java.lang.OutOfMemoryError: PermGen space 不是内存不够,而是类元数据撑爆了固定大小的持久代。常见于频繁热部署(比如 Tomcat 下反复 reload 应用)、大量动态生成类(如使用 CGLIB、Groovy、某些 ORM 的代理机制),或加载了太多第三方库(尤其含自定义类加载器的 SDK)。
- 持久代大小默认只有 64MB(32 位 JVM)或 82MB(64 位),且无法自动扩容
- 类卸载条件苛刻:不仅类实例要被回收,其
ClassLoader也必须不可达,而很多框架会持有对ClassLoader的强引用 -
-XX:MaxPermSize只能临时缓解,治标不治本;设太大反而拖慢 Full GC
升级 JDK 8+ 后元空间(Metaspace)的行为变化
JDK 8 彻底移除了持久代,改用本地内存管理的元空间,关键差异在「自动增长」和「类卸载更积极」:
- 元空间默认无上限(受限于系统内存),但可通过
-XX:MaxMetaspaceSize控制 - 初始大小由
-XX:MetaspaceSize决定(JDK 8 默认约 20.8MB),达到该阈值才触发 GC;未达阈值时即使有垃圾也不会回收 - 类卸载仍依赖 GC,但元空间 GC 会更主动尝试回收无用类(前提是其
ClassLoader确实已死)
注意:-XX:MaxPermSize 在 JDK 8+ 完全无效,若保留会直接报错:Unrecognized VM option 'MaxPermSize=256m'
迁移时必须调整的 JVM 参数
升级后不能照搬旧参数,否则要么启动失败,要么元空间失控:
立即学习“Java免费学习笔记(深入)”;
- 删除所有
-XX:MaxPermSize、-XX:PermSize - 显式设置
-XX:MetaspaceSize(建议 128m–256m),避免早期频繁 GC - 设置
-XX:MaxMetaspaceSize(建议 512m–1g),防无限增长拖垮机器 - 若应用确有大量动态类(如 Spring Boot + DevTools + Thymeleaf 模板热编译),可加
-XX:+AlwaysPreTouch提前分配内存,减少运行时抖动
示例合规启动参数:
-Xms2g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=1g -XX:+UseG1GC
升级后仍出现 java.lang.OutOfMemoryError: Metaspace 怎么办
这说明不是参数问题,而是类泄漏——新瓶装旧酒:
- 检查是否仍有未释放的
ClassLoader:用jcmd <pid> VM.native_memory summary</pid>或 MAT 分析 heap dump 中的ClassLoader实例数是否持续上涨 - 排查动态代理滥用:CGLIB 的
Enhancer、JDK 动态代理的Proxy.newProxyInstance每次都生成新类,应复用或缓存 - 验证第三方 SDK 是否“假卸载”:比如某些监控 agent、日志插件会在 shutdown 时漏掉清理逻辑
- Tomcat 用户重点看
webappClassLoader是否重复创建:检查context.xml是否误配了reloadable="true",或存在未关闭的线程池持有 classloader 引用
元空间溢出往往比 PermGen 更难定位,因为错误发生时堆内存可能很空,容易误判为“没内存”,实际是类加载器链没断。










