Java堆外内存排查
启动参数
java -XX:NativeMemoryTracking=detail -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+AlwaysPreTouch -XX:ReservedCodeCacheSize=128m -XX:InitialCodeCacheSize=128m -Xss512k -Xmx1g -Xms1g -XX:+UseG1GC -XX:G1HeapRegionSize=4M -jar lib/helfy-1.0-SNAPSHOT.jar
现象
程序启动一段时间,就会被自动kill掉。
分析
从上面的jstat -gcutil 现象来看,应该不是heap OOM,也不是 Metaspace OOM。 那可能是堆外内存OOM,怎么验证呢? 通过top分析Java进程内存占用当这个内存到了3.7g多的时候,程序就会被kill掉。 通过jmap -heap 104139 查看Java堆内存占用我们也可以通过pmap来查看进程的内存占用。 pmap -x 104139 | tail -n 1通过jmap和pmap可以发现,pmap的内存占用比jmap的堆配置总和还多。 而且pmap的RSS也一直在涨。
解释一下这个RSS的含义,如下图。一般来讲共享库所占的内存应该不会很多https://www.jianshu.com/p/3bab26d25d2e
这里基本就能确定是堆外内存OOM了
默认来讲,堆外内存大小=设置的堆内存大小-XX:Xmx,但是从top的结果来看,基本上在3.7多就被kill了。 堆内存设置的是1g,然后metaspace设置的好像是256m。那如果堆外内存是1g的话,感觉说不太通。
为什么会发生堆外内存OOM呢?看看线程到底在做什么 先top一把 top -H 打印所有线程的使用情况
这个 280376一直是用了88%的cpu,稳居榜首。 通过jstack看看它到底在干嘛(这里存在一个十进制转16进制的转换) jstack 280375 | grep ‘44738’ -A 40从业务代码可以看出,应该是这个one.helfy.SleepyBean里面一直调用springboot的方式去解压jar包。用的是Inflater这个类,一直在申请堆外内存。
祖传命令
# 观察GC情况
ps -afx | grep java | grep -v grep | awk '{print $1}' | xargs -I{} jstat -gcutil {} 1s
# 从JVM角度观察内存
jcmd <pid> VM.native_memory detail
# 观察内存使用情况
pmap -x 1
# 排序一下内存占用
pmap -x 1 | sort -n -k3
# 跟踪系统调用(看谁在分配内存)
strace -f -e 'brk,mmap,munmap' -p <pid>
总结
Top + jstack 分析cpu高负载场景 假设Java进程pid=60
- top -H 60 ,找到一个pid=280376的高负载线程
- printf %x 280376 | xargs -I {} sh -C “jstack 60 | grep {} -A 40”
参考
https://tech.meituan.com/2019/01/03/spring-boot-native-memory-leak.html (案例来源) https://tech.meituan.com/2020/11/12/java-9-cms-gc.html (处理思路: 场景九)