1.问题简述

今天同事让我帮忙解决一个异常的服务,其表现如下:

  1. 服务V最大内存8GB。
  2. 服务某接口的压测结果:最大QPS为200左右。
  3. 通过Spring Boot Admin监测到服务间歇性由状态UP变成OFFLINE
  4. 通过Spring Boot Admin监测到服务的状态变更是由于YGC导致的,JVM并未崩溃。
  5. 通过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文件拷贝至本地

  1. 将dump文件从服务端拷贝至堡垒机。
  2. 将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%不变。
      在这里插入图片描述
2.7.2其他解决方案
  • 还在测试环境对继续使用分库分表数据源的方案进行了测试,过程就是修正他之前错误的配置。
  • 测试之后,发现问题同样解决,与实际解决方案的表现基本一致。

3.总结

  • pom.xml中的依赖建议按需配置,不要去拷贝别人的配置,这不是一个好习惯。
  • pom.xml中冲突的但是不影响使用的依赖也要及时排除,不要项目能run就放任不管,这不是一个好习惯。
  • 配置文件中的每个配置项的作用是什么自己要弄清楚,不要去拷贝别人的配置,这不是一个好习惯。
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐