这两天线上某系统出现了频繁的Full GC,应用频繁暂停。导致业务不可用。
下面贴出分析的思路(部分内容非自己分析)
1.确认Full GC原因
应用Full GC最频繁的阶段,刚好业务量出现了一定的猛增,会不会导致heap临时不够用呢?带着这个疑问大概看了下Full GC的开始时间和应用业务量猛增的时间,发现不吻合,基本上就排除了由于业务量增加导致临时heap不够用的情况。
于是在看看应用一个月的内存走势,有趣的问题发生了。内存在一个月的时间里,平稳的增加了400M左右,每天大概增加10~20M左右,比较规律。而且分析应用的JVM参数,GC配置为CMS频繁的回收,如果对象已死,应该会被回收,heap应该呈现平稳的曲线;因此可以推断:heap中有大量存活的对象,导致CMS不能被回收,随着时间的推进,内存不断增加。
2.使用mat确认内存泄露点
(自己在泄露点的分析时,带有了一些思维的定式,没有仔细查看dump文件中内存对象)。这一步是确认有时比较简单,有时比较困难,关键时dump的时间点问题。
故障现场的dump文件时最快解决问题的途径,因为整个heap都被泄露资源占满。dump文件可以轻而易举的发现泄露点。
但是,如果故障线程的dump文件没有,我们怎么分析呢?思路大概是这样的:
1.确认内存泄露增量,比如30天400M,每天大概就为10~20M。
2.dump两份时间点的heap情况,两份dump文件时间间隔以内存泄露增量作为参考。如果一天泄漏量就很多,那么只需要dump两份文件。查看两天内存对象整理的情况。
比如下面的两张图:
系统运行了一天的dump图
系统运行了5天的dump图
从图中我们很容易的看出httpclient存在了内存泄露,和httpclient相关的内存占用了大概80M,这和我们的内存泄露增量很匹配,因此这个点就是需要警觉然后深入去看的点。
3.分析为什么存在泄露点
在我所遇到的内存泄露中,多数都是由于Collection的使用造成的。不断的往容器中放对象,而造成对象不能被清理,随着时间的推进,内存不断被蚕食。
上面的例子中最后的原因就是应用不断的往HashMap中放对象造成的。HashMap总的key没有重写equals方法,导致每次new的对象都会被put到map中。
所以看似简单的事物,往往就是隐患,往容器(特别是全局共享的容器对象)放对象一定要小心。
一些经验型的总结:
1.保护好故障现场。故障现场的数据,是我们解决问题最有效的途径。
2.排除干扰因数。
3.分析泄露特点
4.dump文件分析保持敏感度,不放过任何机会。
5.容器类对象,往往是造成泄露的罪魁祸首。