看到OutOfMemoryError: Metaspace,说明JVM类元数据区耗尽,与堆内存无关;需先确认错误后缀为Metaspace,再通过jstat -gc查看MU/MC是否趋近满载,结合jcmd或JConsole验证占用,并定位反射、动态代理等高频加载且无法卸载的类源头。

看到 OutOfMemoryError: Metaspace,别急着加参数。它不是堆内存不够,而是 JVM 存“类蓝图”的地方满了——比如类加载太多、卸不掉、或者根本没设计好释放机制。
看错误后缀,确认是 Metaspace 问题
这是排查的第一步,也是最关键的一步。只要报错末尾明确写着 Metaspace(Java 8+),就说明问题出在类元数据区域,和堆内存无关。它不走 GC 流程,一旦超限直接抛异常、终止线程甚至整个进程。
- 不是
Java heap space,所以调-Xmx没用 - 不是
Direct buffer memory,所以不用查 NIO 缓冲区 - 错误信息里没有
PermGen字样,说明你用的是 Java 8 或更高版本
查实时占用,判断是否真满
光看报错不够,得验证 Metaspace 实际用了多少。推荐几种轻量、无需重启的方式:
-
jstat -gc <pid>:关注MU(Metaspace Used)和MC(Metaspace Capacity)两列,如果 MU 持续接近或等于 MC,且 MC 已达-XX:MaxMetaspaceSize设定值,基本就是满了 -
jcmd <pid> VM.native_memory summary:查看 native memory 中Class区域的使用量,异常偏高是重要线索 - JConsole 或 VisualVM 连上应用,直接看
java.lang:type=MemoryPool,name=Metaspace的Usage.used值
找加载源头,识别高频类增长点
Metaspace 溢出的本质是“类越积越多,卸不干净”。常见场景有:
立即学习“Java免费学习笔记(深入)”;
- Spring Boot 热部署(DevTools)反复启动,旧类加载器未回收
- CGLIB/ByteBuddy 动态代理生成大量匿名类(如 AOP、Hibernate lazy proxy)
- OSGi 或模块化系统频繁 install/uninstall bundle
- 运行时编译 Groovy/Scala 脚本,每次执行都生成新类
- Tomcat 部署多个 WAR 包,旧版本未彻底清理,类加载器残留
临时加启动参数 -XX:+TraceClassLoading -XX:+TraceClassUnloading,观察日志中哪些类被反复加载却极少卸载,能快速定位泄漏点。
调参与修复,分两步走
参数调整只是辅助手段,核心是解决类加载行为本身:
- 生产环境必须设
-XX:MaxMetaspaceSize=256m(中小应用)或512m(Spring Cloud 微服务),避免吃光系统内存 - 搭配
-XX:MetaspaceSize=128m,控制首次触发 Metaspace GC 的阈值,平衡启动性能和早期预警 - 启用类卸载:
-XX:+ClassUnloadingWithConcurrentMark(G1 GC 下有效),让 GC 尝试回收无用类元数据 - 检查自定义类加载器:确保重写
finalize()或显式调用close(),避免引用链阻止卸载 - 减少动态类生成:比如用接口+策略模式替代大量 CGLIB 代理,或缓存已生成的代理类


















