移动学习网 导航

技术侦探日记 01 - FULL GC篇

2024-05-19m.verywind.com
~

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

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

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

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

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

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

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

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

处理方案:

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

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

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

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

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

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

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

printf "%x
" [线程id]

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

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

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

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

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

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



户户网菜鸟学习
联系邮箱
返回顶部
移动学习网