一、背景说明
- 应用框架:spring boot 2.3.12.release
- jdk 版本:oracle jdk 1.8(如
jdk1.8.0_231) - 问题现象:metaspace 内存占用超过 200mb,存在潜在内存泄漏或类加载异常风险
metaspace 简介:
自 jdk 8 起,永久代(permgen)被移除,取而代之的是 metaspace。它用于存储类的元数据(如 class、method、field 等),默认使用本地内存(native memory),理论上可动态扩展,但若类加载器未正确卸载,仍可能导致 metaspace oom。
二、诊断命令与工具
1. 使用jcmd查看 native memory 分布(含 metaspace)
前提:启动 java 应用时需开启 native memory tracking(nmt)
启动参数示例:
nohup /home/jdk/jdk1.8.0_231/bin/java \
-xx:+unlockdiagnosticvmoptions \
-xx:nativememorytracking=summary \
-xmx${run_max_size} \
-xms${run_size} \
-xx:+heapdumponoutofmemoryerror \
-xx:heapdumppath=${dump_log} \
-xx:+printgc \
-xx:+printgcdetails \
-xx:+printgctimestamps \
-xx:+usegclogfilerotation \
-xx:numberofgclogfiles=5 \
-xx:gclogfilesize=10m \
-xloggc:safe_manage_gc.log \
-dserver.port=${app_port} \
-jar ${app_name} >/dev/null 2>&1 &执行 nmt 汇总分析:
/home/jdk/jdk1.8.0_231/bin/jcmd {pid} vm.native_memory summary将
{pid}替换为实际进程 id(可通过jps或ps获取)
输出中重点关注class和metaspace相关内存使用量
📌 示例输出片段:
total: reserved=1500mb, committed=800mb
- class (reserved=300mb, committed=250mb)
(classes #20000)2. 使用jcmd分析已加载的类统计信息
命令:
jcmd {pid} gc.class_stats > /tmp/gc_class_stats.txt此命令输出每个已加载类的详细信息,包括:
- 类名
- 加载该类的 classloader
- 字节码大小
- 是否活跃(是否被引用)
可通过分析
/tmp/gc_class_stats.txt定位异常加载的类或重复加载的类(如动态代理、groovy 脚本、热部署等场景)
3. 本地开发环境调试(idea)
在 run/debug configurations → vm options 中添加以下参数,便于观察类加载行为:
-xx:nativememorytracking=summary -xx:maxmetaspacesize=256m -xx:+traceclassloading -xx:+traceclassunloading
-xx:+traceclassloading:打印每个被加载的类-xx:+traceclassunloading:打印被卸载的类(需配合 gc 触发)- 设置
maxmetaspacesize可加速复现 metaspace oom 问题
三、使用 arthas 进行运行时分析
arthas 是阿里开源的 java 诊断工具,适合生产环境实时排查。
1. 下载 arthas
curl -o https://arthas.aliyun.com/arthas-boot.jar
2. 启动 arthas
java -jar arthas-boot.jar
3. 选择目标进程并分析类加载情况
进入 arthas 控制台后,输入以下命令:
# 查看类加载统计 classloader # 查看某个 classloader 加载的所有类(例如 spring 的 launchedurlclassloader) classloader -l # 查看特定类是否被加载 sc com.example.yourclass # 查看类加载器树形结构(有助于识别 classloader 泄漏) classloader -t
重点关注:
- 是否存在大量动态生成的类(如 cglib、asm、javassist 生成的代理类)
- 是否有自定义 classloader 未被回收
- spring boot devtools、groovy、脚本引擎等组件可能频繁加载类
四、常见 metaspace 膨胀原因
| 原因 | 说明 | 排查建议 |
|---|---|---|
| 动态类生成过多 | 如 cglib 代理、lambda 表达式、groovy 脚本 | 检查 gc.class_stats 中类名是否含 $proxy、$lambda 等 |
| classloader 泄漏 | web 应用热部署、模块化加载未清理 | 使用 arthas classloader -t 查看 classloader 实例数 |
| 第三方库缺陷 | 某些 orm、规则引擎缓存类元数据 | 升级依赖或限制其使用范围 |
| mybatis映射自然增长 | mybatis映射sql结果到实体类,mybatis + 反射 → 大量 generatedmethodaccessor | 超过15次后,会生成generatedmethodaccessor,一个功能到极限后就不会再增长数量,每个字段的get set方法都会生成 |
五、优化建议
设置 metaspace 上限(防止无限增长):
-xx:maxmetaspacesize=256m
避免不必要的动态类生成:
- 减少运行时字节码操作
- 避免在循环中定义 lambda 或匿名类
定期分析类加载情况:
- 生产环境定期执行
jcmd pid gc.class_stats - 对比不同时间点的类数量变化
- 生产环境定期执行
如果服务本身非常大,功能非常多,class数量和metaspace会一直增长到某个极限就不会再增长,结合idea本地测试即可
六、附录:参考图示

图中显示 metaspace 已使用 210mb,且 class 区域占比高,提示可能存在大量类加载。
通过以上方法,可系统性地分析和优化 java 应用的 metaspace 内存使用,有效预防 java.lang.outofmemoryerror: metaspace 异常。
总结
到此这篇关于java metaspace空间内存超详细分析的文章就介绍到这了,更多相关java metaspace空间内存内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论