k8s环境,有个服务频繁重启,经过排查日志和事件,确认时由于OOM导致服务重启,为了方便研发定位OOM的具体原因,需要在OOM发生时自动生成内存快照(Heap Dump),以供后续研发分析。

方法一:JVM参数
JVM是提供了一些参数,能在发生OOM时自动生成Heap Dump:
- -XX:+HeapDumpOnOutOfMemoryError
- -XX:HeapDumpPath=/app/oom/java_heapdump.hprof
然而,目前存在两个问题:
- 多次重启会导致heap dump文件覆盖,例如,服务发生了两次OOM,第二次生成的heap dump会覆盖第一次的。
- 这个参数仅用于JVM内部的OOM,而k8s主动重启服务时并不能生成heap dump。我们通过kubectl describe pod 查看重启原因,发现是因为健康检查危响应导致的k8s重启服务。这类情况并非JVM的OOM,因此上述参数无法帮助生成内存快照。
所以如果只是单次分析OOM的原因,可以使用JVM的参数,在java启动参数里加上 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/oom/java_heapdump.hprof,再把容器内的/app/oom/目录挂载到node上即可。
1 | volumes: |
方法二:preStop钩子
为了解决k8s重启时无法导出heap dump和二次覆盖的问题,我们可以通过配置preStop钩子,在容器停止前生成内存快照。相关命令如下:
获取进程ID为10的程序的堆栈信息:
1 | jstack -F 10 /logs/thread.dump |
生成堆内存快照:
1 | jmap -dump:format=b,file=/usr/src/logs/dump.hprof 10 |
通过这样的优化,既能避免heap dump被覆盖,又能在k8s重启时生成有用的内存快照,帮助排查问题。
命令:
1 | jstack -F $(jps |grep -v Jps | awk '{print $1}') | tee -a /usr/src/logs/thread.dump && jmap -dump:format=b,file=/usr/src/logs/$(date +'%Y-%m-%d_%H%M%S').hprof $(jps |grep -v Jps | awk '{print $1}')" |
pod的yaml文件配置
加上以下配置:
1 | lifecycle: |
验证
手动执行kubectl delete pod,可以查看到生成的文件:

两个命令的区别:

