元空间oom本质是类加载失控或卸载失败,需通过jstat、jcmd等工具监控mcu/mcm比值及native memory,聚焦动态代理泛滥、热部署残留、jsp编译三类高危场景排查,堆dump无效。

元空间 OOM(java.lang.OutOfMemoryError: Metaspace)不是堆内存问题,堆 dump 几乎无用。它反映的是 JVM 加载的类元数据(类名、方法、字段、常量池等)超出了元空间容量,本质是类加载失控或卸载失败。
看日志和监控,确认是不是真 Metaspace 问题
错误信息必须明确含 Metaspace,不能只看“OOM”就下结论。同时观察:
- 应用重启后元空间使用率不降反升——说明旧 classloader 没被回收,类在重复加载
- 压测时
jstat -gc <pid></pid>显示 MCU(Metaspace Used)持续上涨,但 MCL(Metaspace Capacity)几乎不变或缓慢增长——说明类在不断加载,卸载极少甚至为 0 - 堆内存充足(比如堆 dump 很小,GC 正常),但依然报 Metaspace OOM——这是典型特征
跳过堆 dump,转向元空间专属诊断手段
别花时间用 MAT 打开 hprof 查类——元数据不在堆里。重点用以下命令:
-
jstat -gc <pid></pid>:关注 MCM(Metaspace Capacity)、MCU(Metaspace Used)、MCU/MCM 比值,连续采样看是否单向增长 -
jcmd <pid> VM.native_memory summary scale=MB</pid>:查看 Metaspace 行的 committed 和 used,确认本地内存真实占用 - 加启动参数
-XX:NativeMemoryTracking=detail后,用jcmd <pid> VM.native_memory detail.scale=MB</pid>追踪元空间内部分配细节(如 class space vs metadata space)
聚焦三类高危场景,快速定位根因
90% 的元空间 OOM 都落在以下场景:
-
动态代理泛滥:CGLIB/ASM/Spring AOP 配置不当,每次请求都生成新代理类(如 proxy 被设为 prototype、未启用 cache)。可检查
Enhancer.setUseCache(false)是否误开 - 热部署残留:应用反复 reload(如 Spring Boot DevTools、Tomcat 热部署),旧 classloader 无法被 GC,其加载的所有类元数据滞留元空间
- JSP 或模板引擎编译:大量 JSP 页面首次访问触发编译,每个 JSP 对应一个 Servlet 类;或 Freemaker/Thymeleaf 模板配置了自动重编译且未清理缓存
调优不是万能解,优先修复代码逻辑
临时缓解可加参数:
-
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m:避免元空间无限制扩张,让 OOM 更早暴露问题 - 禁用 CGLIB 缓存时务必确认是否真需要——
setUseCache(false)是常见陷阱 - 热部署场景确保 classloader 能被回收:避免静态持有、线程局部变量(ThreadLocal)引用旧上下文、未关闭的 JDBC Driver 注册
重启只能清空当前元空间,不能解决重复加载问题。真正治本,是切断无效类加载路径。











