如何获取Metaspace使用直方图以排查元空间内存溢出问题?
解决方法
一、使用JDK原生工具jcmd统计Jar包级元空间占用
步骤1:获取目标进程ID
执行以下命令列出Java进程:
jps
或直接用jcmd查看所有进程:
jcmd
步骤2:导出类的元空间统计数据
替换<pid>为你的进程ID,执行命令导出详细类元数据:
jcmd <pid> GC.class_stats > class_stats.txt
该输出包含每个类的元空间占用、类加载器、所属Jar包路径等核心信息。
步骤3:按Jar包汇总占用数据
创建jar_metaspace_stats.awk脚本文件:
/^[0-9]+/ { jar_path = $NF class_size = $5 total[jar_path] += class_size } END { for (jar in total) { printf "%.2f MB\t%s\n", total[jar]/1024/1024, jar } }
执行脚本并按占用从大到小排序:
awk -f jar_metaspace_stats.awk class_stats.txt | sort -nr
输出示例:
128.50 MB /path/to/spring-core-5.3.20.jar 96.20 MB /path/to/hibernate-core-5.6.10.Final.jar ...
二、结合堆转储与jhat分析元空间占用
步骤1:生成堆转储文件
替换<pid>为进程ID,生成包含元空间信息的堆转储:
jmap -dump:live,format=b,file=heap_dump.hprof <pid>
步骤2:用jhat查询汇总数据
启动jhat服务:
jhat heap_dump.hprof
浏览器访问http://localhost:7000,进入OQL Console,执行以下查询按Jar包汇总:
SELECT cls.source AS jar_path, sum(cls.instanceKlassSize)/1024/1024 AS total_mb FROM java.lang.Class cls WHERE cls.source IS NOT NULL GROUP BY cls.source ORDER BY total_mb DESC
若需按包名统计,可修改为:
SELECT substring(cls.name, 0, indexOf(cls.name, '.')) AS package_name, sum(cls.instanceKlassSize)/1024/1024 AS total_mb FROM java.lang.Class cls GROUP BY package_name ORDER BY total_mb DESC
三、Spring代理类的专项统计
由于应用大量使用@Inject会生成CGLIB/JDK代理类(类名含$$EnhancerBySpringCGLIB$$或$Proxy),这类类的source字段通常为空,可单独统计:
awk '/\$\$EnhancerBySpringCGLIB\$\$|Proxy$/ {sum += $5} END {print "Proxy classes total: " sum/1024/1024 " MB"}' class_stats.txt
额外建议
- 服务器自身已占用300MB元空间,剩余212MB不足以承载应用类+代理类,结合统计结果可精简未使用的Jar包,降低类总量。
- 检查Spring代理逻辑,确认是否存在对无需代理的类过度使用注解的情况,减少动态生成类的数量。
内容的提问来源于stack exchange,提问作者SATO Yusuke
相关产品推荐
相关产品推荐

