生产环境服务A部署在K8s上,某天运维告诉我这个服务经常会重启,客户没有报告是因为我们是滚动发布更新,先启动这个服务的一个新实例,然后将旧实例Kill掉,这样前端是无感知的,但重启是实实在在存在的,生产问题不可马虎,于是开启了定位问题之旅。
定位问题前前后后一共花了快一个月,过程如下:


jmap,jstack等命令,实施前才想到,我们用的基础镜像是包含jre的,不包含jdk,根本不支持jmap,jstack等命令,然后找运维去帮忙换成JDK的基础镜像,被拒绝了,只能自立更生了,在网上看到了Jattch这个东西,于是在SIT环境测试了下,居然真可以用,详情可参见 我的另一篇文章Docker容器只有JRE没有JDK使用Jattach导出内存快照 最终使用到的脚本是#!/bin/sh
# 导出当前内存信息
jattach 1 dumpheap /opt/dump/dumpheap_"$HOSTNAME"_`date +%Y%m%d-%H%M%S`.hprof
# 导出当前线程信息
for i in `seq 3`
do
jattach 1 threaddump > /opt/dump/threaddump_"$HOSTNAME"_`date +%Y%m%d-%H%M%S`.log && sleep 1
done
# 导出当前使用CPU最高的线程
top -H -p 1 -n 3 -c -b > /opt/dump/cpudump_"$HOSTNAME"_`date +%Y%m%d-%H%M%S`.log
那么我们来分析下唯一可以打开的这个文件,使用MAT(MemoryAnalyzer Tool)
载入后如下:

可以明显看出有个线程池的某个线程居然内存占用达到2.5G,结合上面Prometheus的老年代一共2.6G可以得知是这个线程把内存吃满了,到底是哪个呢?点击Reports下的Leak Suspects,可以看到:

看到这里就比较清楚了,下面有方法的调用栈,如果看详细的,可以点See stacktrace查看详细调用栈。

至此,问题定位到,剩下的就是去看逻辑,优化代码了。