OutOfMemoryError: Metaspace 表明类元数据区耗尽,与堆内存无关,需定位频繁加载且无法卸载的类,结合 jstat/jcmd/JConsole 验证占用,通过 -XX:MaxMetaspaceSize 等参数合理设限并修复类加载泄漏。

看到 OutOfMemoryError: Metaspace,说明 JVM 类元数据区已耗尽,和堆内存完全无关。它不走常规 GC 流程,一旦超限就直接抛异常、中断线程甚至进程。解决关键不是加堆内存参数,而是定位“哪些类在疯狂加载却卸不掉”,再配合合理参数控制边界。
确认确实是 Metaspace 问题
这是排查起点,必须严格核对错误信息末尾是否明确为 Metaspace(Java 8+):
- 不是
Java heap space→ 调-Xmx没用 - 不是
Direct buffer memory→ 不用查 NIO 缓冲区 - 没有
PermGen字样 → 确认是 JDK 8 或更高版本
实时验证 Metaspace 是否真满
不能只信报错,要查实际占用。推荐以下轻量、无需重启的方式:
-
jstat -gc <pid>:重点看MU(已使用)和MC(当前容量)。若MU持续逼近或等于MC,且MC已达-XX:MaxMetaspaceSize设定值,基本可判定溢出 -
jcmd <pid> VM.native_memory summary:需启动时加-XX:NativeMemoryTracking=summary。输出中关注Class行的 committed 值,异常偏高是强信号 - JConsole 或 VisualVM 连上应用,直接查看
java.lang:type=MemoryPool,name=Metaspace的Usage.used值
定位高频加载又无法卸载的类源头
Metaspace 溢出本质是“类越积越多,卸不干净”。常见场景包括:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- Spring Boot DevTools 热部署反复启动,旧类加载器未回收
- CGLIB / ByteBuddy 动态代理(如 Spring AOP、Hibernate 延迟代理)生成大量匿名类
- Tomcat 部署多个 WAR 包,旧版本未彻底清理,类加载器残留
- 运行时编译 Groovy/Scala 脚本,每次执行都生成新类
- OSGi 或模块化系统频繁 install/uninstall bundle
临时加参数 -XX:+TraceClassLoading -XX:+TraceClassUnloading,观察日志中哪些类被反复加载却极少卸载,能快速圈定泄漏点。
参数配置与修复策略
参数只是辅助,核心是修正类加载行为本身:
- 生产环境必须设
-XX:MaxMetaspaceSize=256m(中小应用)或512m(Spring Cloud 微服务),防止吃光系统内存 - 搭配
-XX:MetaspaceSize=128m,控制首次触发 Metaspace GC 的阈值,兼顾启动性能和早期预警 - G1 GC 下启用
-XX:+ClassUnloadingWithConcurrentMark,让 GC 尝试回收无用类元数据 - 慎用第三方库,尤其含字节码增强能力的框架;动态生成类逻辑务必做压力测试

















