java 8 彻底移除了永久代(permgen),改用本地内存管理的元空间(metaspace)存储类元数据,解决其固定大小、难卸载、易溢出等根本问题;默认自动扩容、gc 触发更合理、类卸载更积极,并要求删除旧参数、显式配置 metaspacesize 和 maxmetaspacesize。

Java 8 彻底移除了永久代(PermGen),改用元空间(Metaspace)存储类元数据,这不是简单改名,而是从设计根源上解决类元数据内存管理的老大难问题。
永久代溢出的根本原因
PermGen 是堆内一块固定大小的内存区域,默认仅 64MB(32 位)或 82MB(64 位),专门存类名、方法、常量池、静态变量、JIT 编译代码等元数据。它不随应用类增长而扩容,一旦加载的类太多——比如 Tomcat 频繁热部署、Spring + CGLIB 大量生成代理类、Groovy 脚本动态编译——就直接抛 java.lang.OutOfMemoryError: PermGen space。这不是堆内存不够,而是这块“小隔间”被塞爆了。
- 类卸载极难:必须同时满足“所有类实例已回收”+“ClassLoader 对象本身不可达”,但多数框架(如老版 Spring、监控 agent)会强持有 ClassLoader,导致类永远卸不掉
- 调参治标不治本:-XX:MaxPermSize 设大了,Full GC 更慢、更卡;设小了,上线就崩
元空间如何从根本上改变规则
Metaspace 不再占用 JVM 堆内存,而是直接使用本地内存(Native Memory),由操作系统管理。关键变化有三点:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 默认自动扩容:不设上限时,可一直增长到系统物理内存耗尽(危险!但至少不会因固定容量突然 OOM)
- GC 触发逻辑更合理:以 -XX:MetaspaceSize(默认约 20.8MB)为初始阈值,首次达到才触发元空间专属 GC;不是每次 GC 都扫它
- 类卸载更积极:只要 ClassLoader 真正死亡,关联的类元数据就能被回收,不再强耦合老年代 Full GC
升级 Java 8+ 后必须做的三件事
旧参数照搬会启动失败或埋下隐患:
- 删掉所有 -XX:PermSize、-XX:MaxPermSize(JDK 8+ 会报错 Unrecognized VM option)
- 显式设置 -XX:MetaspaceSize=256m(避免早期频繁 GC)
- 务必设置 -XX:MaxMetaspaceSize=1g(防失控增长拖垮整机)
推荐启动参数示例:
-Xms2g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=1g -XX:+UseG1GC
升级后还 OOM Metaspace?那不是配置问题,是泄漏
出现 java.lang.OutOfMemoryError: Metaspace,说明类在持续加载却没释放,典型表现是 ClassLoader 实例数不断上涨:
- 用 jcmd
VM.native_memory summary 查看 Metaspace 当前用量 - 导出 heap dump,用 MAT 检查 org.springframework.boot.devtools.restart.classloader.RestartClassLoader 或自定义 URLClassLoader 实例是否堆积
- 排查 CGLIB Enhancer、Javassist CtClass.toClass() 是否在循环中无缓存创建新类
- 检查第三方 SDK(如某些日志插件、APM agent)是否 shutdown 时未清理 ClassLoader 引用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










