确认Metaspace溢出需先看异常堆栈是否为java.lang.OutOfMemoryError: Metaspace,再结合jstat -gc查看MU/MC增长趋势、GC日志中Metadata GC Threshold触发频次及jcmd VM.native_memory summary验证类元数据占用。

Java 8 及以后版本用 Metaspace 替代了永久代(PermGen),用于存放类的元数据(如类名、字段、方法、常量池等)。当加载的类太多、动态生成类过多(如大量使用反射、CGLIB、Groovy、Spring AOP、热部署等),又没及时卸载,就容易触发 OutOfMemoryError: Metaspace。
如何确认是 Metaspace 溢出
看堆栈信息是否明确包含 java.lang.OutOfMemoryError: Metaspace,而不是普通的堆溢出或 GC 相关错误。同时可配合 JVM 启动参数开启详细 GC 日志和元空间统计:
-
-XX:+PrintGCDetails:输出 GC 详情,包括 Metaspace 使用情况 -
-XX:+PrintGCTimeStamps:加上时间戳便于分析 -
-Xlog:gc*:file=gc.log:time,tags(JDK 9+)或-XX:+UseGCLogFileRotation(旧版):方便长期追踪
运行中也可用 jstat -gc <pid> 查看 Metaspace 使用量(MS、MU 列),或用 jcmd <pid> VM.native_memory summary(需开启 -XX:NativeMemoryTracking=summary)辅助判断。
常用调参方式(启动时设置)
Metaspace 默认无上限(只受本地内存限制),但生产环境必须设限,避免耗尽系统内存。关键参数如下:
立即学习“Java免费学习笔记(深入)”;
-
-XX:MaxMetaspaceSize=256m:硬性上限,强烈建议显式设置(如 128M–512M,视应用规模而定) -
-XX:MetaspaceSize=128m:初始分配阈值,首次达到后触发 GC;设太小会导致频繁初始 GC,设太大则延迟 GC,一般设为 Max 的 1/2~1/4 -
-XX:MinMetaspaceFreeRatio=40和-XX:MaxMetaspaceFreeRatio=70:控制 GC 后保留空闲比例,避免反复扩容缩容(默认值通常够用,一般不需改)
示例启动参数:-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m
定位问题根源(不止靠调参)
参数只是兜底手段,根本要查“为什么类越来越多”:
- 检查是否有框架频繁生成代理类(如 Spring Boot + CGLIB + @Configuration 类过多、@EnableAspectJAutoProxy 开启且切面泛滥)
- 排查动态编译场景(如 JSP、Groovy 脚本、模板引擎热重载、某些 RPC 框架的动态 Stub 生成)
- 确认类加载器泄漏:自定义 ClassLoader 加载类后未被回收(常见于 Web 容器热部署、OSGi、插件化系统),可用
jmap -clstats <pid>查看各 ClassLoader 实例数和加载类数 - 用
jcmd <pid> VM.class_hierarchy -all或 MAT 分析 heap dump 中的java.lang.Class和java.lang.ClassLoader对象分布
其他实用技巧
临时应急或验证问题时可启用:
-
-XX:+TraceClassLoading和-XX:+TraceClassUnloading:打印类加载/卸载日志(注意性能开销大,仅调试用) -
-XX:+PrintAdaptiveSizePolicy:观察 Metaspace 自适应策略行为 - JDK 8u40+ 支持
-XX:MaxMetaspaceExpansion控制每次扩容步长,默认 4M,过大可能造成碎片,过小增加 GC 频率
不复杂但容易忽略:Metaspace 占用的是本地内存(native memory),jps/jstat 看不到它对物理内存的影响,需结合 pmap -x <pid> 或 top 的 RES 值综合判断。


















