记一次FullGc调优

起因

自从3月25日起频繁收到线上告警短信,内容为线上某应用的两台实例频繁进行FullGC。
贴其中一种GC监控图

于是上到生产环境

1
ps -ef |grep java
1
sudo jstat -gcutil ${pid} 1000

两条命令下去,随机发现实例Old区占用率已然100%,并且从应用重启至今已经发生了3000+次full gc。并且平均3秒会进行一次full gc。
很明显是发生了死循环创建对象之类的问题。

查找问题

首先重启了一下线上的两台实例(一台一台来,总共就两台),发现没过多久GC又上来了,看来是必现问题。既然如此只好dump了,于是使用

1
sudo jmap -dump:format=b,file=文件名 ${pid}

这里有一个问题,因为我是跳板机进入的生产环境服务器,然而项目是运行在jetty用户下的,因此这里需要将命令更换一下

1
sudo -u jetty jmap -dump:format=b,file=文件名 ${pid}

dump出文件大概4.3个G,下到本地,然后使用 visualVM打开,豁然开朗


可以看到明显是有一个对象数组引用了3174W+的字符串,导致堆内存被塞满,原因很顺利的找到了。

解决

于是首先想到的是查看这个应用的发布记录,于是可以查到这两天这个应用有过两次发布,将两次发布合并与之前版本比较发现确实是修改了部分代码。

代码与某一个营销活动相关。于是将GC突然增长的点与该活动发布记录相对比,时间完全吻合。

叫上小伙伴一起review代码,发现代码中有显示的while(true)循环,break条件是分页查询数据库直到数据库查询没有数据。看来肯定是这个条件的问题
于是切换到底层SOA,发现查询数据库的分页代码存在严重的逻辑漏洞(还是比较明显的,看来团队测试质量还是需要提高一下)。

因为分页查询的漏洞,导致了数据库查询永远都只查第一页的数据,如果一次查询10K条,那么2次20K,30次30K,无限循环下去。

于是注释代码(保留现场,毕竟不是自己的bug),发布底层SOA应用,然后重启出问题的应用实例。完美解决