java内存分析

  最近碰到一个问题,java程序运行过程中,被系统给强行杀死,因为内存占用过大。但是看监控堆内存并没有超出界限,而且还能被定期回收。经过排查研究发现是元空间占用的内存过大导致的。
  首先要明白,java进程占用的内存并不等于java堆内存,堆内存只是其中一部分。

java 占用内存构成分析

内存区域 作用
堆内存-heap 存储对象、数组等数据,通过参数-Xms/-Xmx控制
非堆内存 元空间Metaspace,JIT编译缓存、类加载数据
本地方法栈 每个线程的调用栈,默认每个线程1m-2m
直接内存 NIO使用的对外内存,默认无上限,不收-Xmx控制
jvm自身运行内存 内核线程、GC线程、日志、监控等等

我们通过命令jstat -gc <PID> 1000 1可以查看JVM内存的详细使用信息,这个命令可以列出堆内存和非堆内存的使用情况。
jcmd <PID> VM.metaspace 查看元空间的详细信息
jstat -gcmetacapacity <PID>查看元空间的使用信息
通过上面两个命令最总到是元空间占用的内存太多,导致进程被系统杀死。
解决方案,我么你可以通过参数-XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1G来限制元空间的大小。
我们还可以查看直接内存使用量
jcmd <PID> VM.native_memory detail
查看线程数量
jstack <PID> | grep "java.lang.Thread.State" | wc -l

如果我们需要进一步排查内存占用情况,我们可以使用以下排查命令

# 查看元空间详细使用
jstat -gcmetacapacity <PID>
# 导出堆快照分析类加载情况
jmap -dump:format=b,file=heap.hprof <PID>
# 用MAT(Memory Analyzer Tool)打开heap.hprof,查看“Class Loader”占用

如果我们想验证新添加的参数是否生效,可以执行一下命令

查看JVM参数是否生效
jcmd <PID> VM.flags | grep -E "Xmx|MetaspaceSize|DirectMemorySize"

我们还可以导出类加载器和方法元数据统计

# 1. 导出类加载器详细统计(替换为你的进程PID)
jmap -clstats <PID> > classloader_stats.txt

# 2. 查看元空间中类的分布(重点看类数量和占用)
# 过滤出占用最高的类(按类数量排序)
cat classloader_stats.txt | grep -E "ClassLoader|classes, bytes" | sort -k3 -r

# 3. 用jcmd查看元空间详细占用(包含方法元数据)
jcmd <PID> VM.metaspace > metaspace_detail.txt
# 查看metaspace_detail.txt,重点看"Class space used"和各模块占用

如果服务程序内存占用过高,临时又无法重启服务,可以采用临时方案让服务程序强制执行垃圾回收

# 强制触发JVM堆和元空间GC(仅临时缓解,无法根治泄漏)
jcmd <PID> GC.run
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容