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会覆盖第一次的。
- dump文件保存在容器内,如果没有把目录挂载到宿主机的话,容器重启文件丢失。
所以如果只是单次分析OOM的原因,可以使用JVM的参数,在java启动参数里加上 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/oom/java_heapdump.hprof,再把容器内的/app/oom/目录挂载到node上即可。
1 | volumes: |
方法二:JVM参数+脚本
为了解决以上文件覆盖以及文件丢失的问题,可以写一个脚本,在发生OOM时把dump文件传到sso或者minio,在这里使用自己部署的minio,具体方法如下:
部署minio服务端
这里使用docker-compose来部署:
docker-compose.yaml:
1 | version: '3' |
启动:
1 | docker-compose up -d |
使用NGINX反向代理:
1 | server { |
访问web界面:

编写shell脚本
编写shell脚本,在pod发生OOM时,执行脚本,把dump文件上传到minio,并告警到企微群:
oom.sh:
1 | #!/bin/bash |
添加JVM参数
启动应用时需要添加以下参数:
1 | -XX:+HeapDumpOnOutOfMemoryError |
参数解释:
1 | -XX:+HeapDumpOnOutOfMemoryError : 当发生内存溢出错误(Out of Memory Error)时,将会生成一个堆转储文件(Heap Dump)。堆转储文件是一个二进制文件,记录了 JVM 堆中对象的详细信息,可以用于分析内存使用情况和调试。使用该选项启用堆转储功能。 |
下载minio客户端
1 | wget https://dl.min.io/client/mc/release/linux-amd64/mc |
测试
让开发写个死循环去请求,触发OOM:
告警:

dump文件:

方法三: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,可以查看到生成的文件:

两个命令的区别:

