记一次服务内存不断飙升的JVM调试过程
1.问题简述今天同事让我帮忙解决一个异常的服务,其表现如下:通过Spring Boot Admin监测到服务间歇性由状态UP变成OFFLINE。通过Spring Boot Admin监测到服务的状态变更是由于YGC导致的,JVM并未崩溃。通过k8s的pod监控发现内存使用率从16:10的15%飙升至17:40的33%。服务V最大内存8GB。服务某接口的压测结果:最大QPS为200左右...
1.问题简述
今天同事让我帮忙解决一个异常的服务,其表现如下:
- 服务V最大内存8GB。
- 服务某接口的压测结果:最大QPS为
200
左右。 - 通过Spring Boot Admin监测到服务间歇性由状态
UP
变成OFFLINE
。 - 通过Spring Boot Admin监测到服务的状态变更是由于YGC导致的,JVM并未崩溃。
- 通过k8s的pod监控发现内存使用率从
30%
短时间内飙升至80%
。
2.解决过程
2.1.查看类直方图
进入容器,通过jcmd
命令先后查询两次类实例直方图。
# 找出pid
[root@c-video-sh-1 services]# jps |grep -v Jps
14 jar
# 第一次打印直方图
[root@c-video-sh-1 services]# jcmd 14 GC.class_histogram |head -9
14:
num #instances #bytes class name
----------------------------------------------
1: 680625 38115000 org.codehaus.groovy.runtime.metaclass.MetaMethodIndex$Entry
2: 522796 18049936 java.lang.Object[]
3: 125100 15801720 char[]
4: 484356 11624544 org.codehaus.groovy.util.FastArray
5: 5926 10355192 byte[]
# 间隔3秒,第二次打印直方图
[root@c-video-sh-1 services]# jcmd 14 GC.class_histogram |head -14
14:
num #instances #bytes class name
----------------------------------------------
1: 699210 39155760 org.codehaus.groovy.runtime.metaclass.MetaMethodIndex$Entry
2: 536856 18443784 java.lang.Object[]
3: 125870 15880432 char[]
4: 497586 11942064 org.codehaus.groovy.util.FastArray
5: 5629 10069288 byte[]
通过两次直方图对比,很容易发现很多类型的实例在"疯狂生长",所以怀疑存在内存泄漏
。
2.2.通过jhat生成dump文件
运行中的进程不方便进行JVM分析,所以通过jhat
导出heapDump文件。
[root@c-video-sh-1 services]# jmap -dump:live,format=b,file=dump_001.bin 14
Dumping heap to /home/services/dump_001.bin ...
Heap dump file created
2.3.将dump文件拷贝至本地
- 将dump文件从服务端拷贝至堡垒机。
- 将dump文件从堡垒机拷贝至本地。
上述步骤可以参考本人另一篇博客:通过堡垒机/跳板机实现文件在本地Mac与服务器之间的互传
2.4.通过MAT(Memory Analyzer Tool)分析dump文件
这里暂时不对MAT的按照与使用进行说明。
通过MAT打开dump文件,在Overview
页面选择打开Leak Suspects
,即:内存泄漏预测,如下:
很明显,org.codehaus.groovy.reflection.ClassInfo
这个类的实例很可能是造成内存泄漏的原因。
2.5.分析pom.xm依赖关系
我询问了同事,哪里用到了这个类org.codehaus.groovy.reflection.ClassInfo
,他跟我说并没有用到。
因此,怀疑是基础服务用到了这个类,类似数据库、框架之类的。
在IDEA
中,通过Option + Shift + Command + U
快捷键打开依赖关系图。
然后通过Commond + F
快速找到groovy
包所在,最终发现了依赖groovy
所在的包:sharding-sphere
。
因为依赖groovy
包的只有sharding-sphere
,所以可以确定是sharding-sphere
出了问题,也就是分库分表数据源。
2.6.最终定位问题所在
通过前面的定位,基本可以确定是因为分库分表的数据源配置不正确导致的。
所以询问了同事,为什么要用分库分表数据源,因为他这个项目不涉及分库分表,他说:项目的pom.xml是拷贝的别人的。。。
好吧,继续查询问题所在,发现他虽然依赖了分库分表的数据源,但是他的配置确实错误的。
最终导致的结果就是数据库连接得不到释放,连接上的数据不会自动回收,导致内存越来越大。
2.7.解决问题
2.7.1.实际解决方案
- 删除分库分表依赖,改用druid。
- 删除pom.xml中根本不会用到的很多依赖。
- 排除pom.xml中很多冲突但不影响使用的依赖。
- 重新上线,问题解决:
- 在Spring Boot Admin中,一直保持
UP
状态。 - 服务某接口的压测结果:最大QPS上升至
3000
左右。 - 在k8s的pod监控,内存使用率维持在
20%
不变。
- 在Spring Boot Admin中,一直保持
2.7.2其他解决方案
- 还在测试环境对
继续使用分库分表数据源
的方案进行了测试,过程就是修正他之前错误的配置。 - 测试之后,发现问题同样解决,与实际解决方案的表现基本一致。
3.总结
- pom.xml中的依赖建议按需配置,不要去拷贝别人的配置,这不是一个好习惯。
- pom.xml中冲突的但是不影响使用的依赖也要及时排除,不要项目能run就放任不管,这不是一个好习惯。
- 配置文件中的每个配置项的作用是什么自己要弄清楚,不要去拷贝别人的配置,这不是一个好习惯。
更多推荐
所有评论(0)