美文网首页
技术侦探日记 01 - FULL GC篇

技术侦探日记 01 - FULL GC篇

作者: 数科贝塔 | 来源:发表于2020-02-26 19:10 被阅读0次

引言

众里寻他千百度,蓦然回首,还是垃圾回收;内存占用过高,cpu负载居高不下,如何高效的借助工具来排查问题,让我们跟随本文来抽丝剥茧,让头疼的垃圾回收和full gc问题浮出水面...
上周四微信上收到智能告警,某应用出现full gc频繁问题,初步观察了下sgm和线上机器情况,定位问题。每次告警都是对我们每个金融消防员的考验:如履薄冰,胆战心惊。

报 警

image

立 案 调 查

  • 分析依据:发生fullGC的最常见情况是老年代或者永久代空间不足时。

  • 现场分析:通过SGM查看老年代和永久代的空间占比剩余空间还有一定比例,不至于发生fullGC,发生原因最后分析。

image

另外sgm上看了下jvm监控,发现堆内存从14号上午窜上去后再没下来过,下面这个图很容易定位是发生了内存泄漏,以下的思路就顺着定位内存泄漏的程序进行

image
  • 排查:看了下mapi代码提交记录,近期无上线,初步排除新上线代码问题。
image

在sure上使用jmap命令,发现char占据大量内存,怀疑存在大字符串。

image

周五找运维下了一份详细的dump文件,使用亮哥之前分享过的IBMHeapAnalyzer工具,分析发现问题可能出在EnterRealNameApplyUploadImgReqModel类里,这个类是用于实名申请时图片上传接口的入参实体类,里面包含了图片的base64的string串,占用较大空间。

image

排查mapi底层biz系统,查看EnterRealNameApplyUploadImgReqModel对应的实现类,发现biz中有对图片大小进行限制,最大为2M,但是mapi无限制,怀疑可能为此接口中上传图片过大。

经磊哥点拨,发现sdk中对base串做了加密,并在mapi中做了解密处理,加解密工具为静态(static)工具方法,可能导致内存泄漏。

image

定位到问题后,再使用亮哥推荐的visualVM插件,在本地启了mapi应用,在sdk写了个死循环去调图片上传接口,并故意将照片设置为3M,同时在idea的VM Option中JVM内存调至300M,此时效果如下:

image

可以很清楚的发现,old区增长速度特别快,同时gc次数频繁,并且无法有效的降低old区占用,old区整体呈现递增趋势,很容易发生内存溢出,经过之前的定位流程,猜测为图片本身较大,在亚当区无法容纳该对象时,直接塞到old区,同时加解密方法为静态方法,被持续引用,导致无法进行垃圾回收,导致old区持续递增。

定 案

处理方案:

  • 生产服务器的内存为8G,将堆内存从2G扩到4G

  • 图片上传接口不在走通用加解密流程,在sdk、mapi单独为其封装了一套特殊的加解密流程,base64串不进行加密,直接做拼接处理,其余参数做加解密。处理后效果如下:

image

处理后可以很明显的发现无论是Old Gen区的递增速度还是gc次数相较于之前发生了很大的变化,趋于正常。

案中案-CPU分析

以上过程其实问题已经得到解决,但发现频繁报fullgc的机器,cpu一直占用在10%以上,怀着打破砂锅问到底的态度对cup的问题也进行了下分析:

1、通过top命令查看占用cpu过高的进程

image

可以看到占用cpu的进程PID为7975

2、通过命令查找到占用cpu最高的线程

命令:top -H -p [进程id] top –H –p 7975

image

3、将线程号转化为16进制(jstack线程堆栈中使用的16进制)

printf "%x\n" [线程id]

image

4、 查找线程号对应的线程

执行: jstack [进程id] |grep -A 10 [线程id的16进制]

image

由上图可以看到,一直在占用CPU的线程是CMS垃圾回收线程,由于堆内存占用过高程序又不释放,垃圾回收线程一直在尝试回收内存导致cpu过高。

并案分析-垃圾回收原因

上面再分析触发垃圾回收的时候留了一个小尾巴,为什么老年代和永久代占用不高的时候频繁的发生了full gc呢。由于此应用使用的是jdk1.6,垃圾回收器使用的是CMS,它是基于“标记--清除”算法实现的,特点是在收集结束的时候会有大量的空间碎片产生。空间碎片太多的时候,将会给大对象的分配带来很大的麻烦,往往会出现老年代还有很大的空间剩余,但是无法找到足够大的连续空间来分配当前对象的,只能提前触发 full gc。如果jdk调整为1.7u4及以上即可使用G1垃圾回收算法不会产生大量的空间碎片。

结 案 总 结

JVM问题一般不是很容易遇到,程序有bug或者并发量大的时候均可能导致jvm异常,通过以上问题的分析过程及以往的经验简单总结下排查jvm问题的一般思路:

  • 查看jvm内存和机器CPU情况

  • 内存占用过高,可能是发生内存泄漏,需要导出dump文件借助mat或者是IBM HeapAnalyze来分析内存中哪些对象占比比较高,那些实例较多的对象需要重点分析

  • cpu占用过高时可以通过步骤4的分析定位到具体的线程,程序编码中用到多线程的地方一定要给线程起个有意义的名字不要用默认的名字,这样出问题时方便定位。

上面只是个大概的流程,具体问题还需具体分析,重点还是需要掌握jvm原理并灵活应用

相关文章

网友评论

      本文标题:技术侦探日记 01 - FULL GC篇

      本文链接:https://www.haomeiwen.com/subject/wlyichtx.html