最明确的信号是日志中出现java.lang.outofmemoryerror: metaspace,表明元空间耗尽导致无法加载新类;需检查maxmetaspacesize配置、观察gc日志中metaspace与full gc共现情况、定位类加载爆炸源头,并通过压测数据合理设置-xx:maxmetaspacesize和-xx:metaspacesize。

直接看日志里的 java.lang.OutOfMemoryError: Metaspace,这是最明确的信号。它说明元空间已耗尽,JVM无法再加载新类——不是堆内存问题,也不是磁盘空间不足,而是元空间配置本身出了硬伤。
确认溢出是否由 MaxMetaspaceSize 设置过小或未设导致
先查 JVM 启动参数:
- 用
jinfo -flag MaxMetaspaceSize <pid></pid>查当前值;返回-XX:MaxMetaspaceSize=-1表示未显式设置(即无上限) - 返回具体数值(如
256m)但业务类数量持续增长,而该值远低于实际所需,就是配置过小 - 对比同类生产实例:若其他同构服务设置了
512m且稳定,而故障实例仍用默认~20–50MB,基本可锁定配置不当
观察元空间动态行为与 GC 关联性
元空间满不一定会立刻 OOM,但会频繁触发 Full GC 来尝试卸载类。关键要看:
- 启用 GC 日志:
-Xlog:gc*:file=gc.log:time,tags,level(JDK 11+)或-XX:+PrintGCDetails -XX:+PrintGCTimeStamps(旧版) - 在日志中搜索
Metaspace和Full GC是否高频共现;例如连续多次Full GC (Metadata GC Threshold) - 用
jstat -gc <pid></pid>实时查看:MU(Metaspace used)接近MC(Metaspace capacity),且MC在缓慢上涨,说明正在反复扩容——这正是未设MaxMetaspaceSize的典型表现
定位类加载爆炸源头
配置不当只是表象,背后往往是类加载失控。需快速圈定“谁在疯狂造类”:
- 加启动参数临时追踪:
-XX:+TraceClassLoading -XX:+TraceClassUnloading,重现场景后 grep 统计加载量(如grep "Loaded" trace.log | wc -l) - 用
jcmd <pid> VM.native_memory summary scale=MB</pid>查 Metaspace 当前占用(注意是 native memory,非堆) - 导出类加载快照:
jcmd <pid> VM.class_hierarchy</pid>或结合jmap -clstats <pid></pid>,看哪个 ClassLoader 实例加载了上千个类(尤其关注WebAppClassLoader、SpringBootDevToolsRestartClassLoader等)
验证并落地修复配置
不要靠猜,用数据定阈值:
- 在准生产环境压测,监控
jstat -gc <pid></pid>中MU峰值,取其 1.5–2 倍作为-XX:MaxMetaspaceSize初始值 - 必须同时设
-XX:MetaspaceSize(如256m),避免首次扩容就触发 Full GC - 上线后持续观察:若
MU长期稳定在MC的 70% 以下,说明配置合理;若仍频繁接近上限,说明存在类泄漏,需进一步分析 ClassLoader
配置错误本身不难改,难的是改完不敢信——所以每一步都要有日志、有数字、有对比。只要看到 Metaspace 相关 GC 明显减少,MU/MC 曲线趋于平缓,就说明致命溢出的风险真正解除了。











