现象

线上环境,起初某一应用系统每隔几天就会出现频繁 Full GC,内存持续升高并且释放不掉,慢请求上升,如果不及时重启会导致服务不可用,经过后续需求陆续上线,出现问题的频率变高,隔两天就会崩。

随后申请了线上临时 root 用户权限,在内存较高时,下线掉一台服务器并 dump 出内存做分析。

内存升高.jpg

排查思路

1、通过命令 dump 内容

jmap -dump:format=b,file=dump.hprof [pid]

2、导入到内存分析工具,如:VisualVM、JProfiler

3、查看大对象,顺藤摸瓜,分析可疑代码

JVM大对象分析.jpg

排查过程

通过 JProfiler 中大对象分析,可以看出有大量的 HashSet对象,检查代码可以发现有好几处方法都使用了 HttpClientUtils httpClientUtils = new HttpClientUtils() 来手动创建对象。

HttpClientUtils 工具类中自定义了一个连接池,和一个 static 的回收连接的守护线程 idleThreadidleThread 中使用 Set 集合管理连接池,并定期回收已失效的连接。

当使用 new 来每次调用都创建一个实例时,Set 集合中对象大量堆积,造成内存占用越来越多,最后导致 Full GC,服务响应变慢。

了解原因后针对这个不规范使用做了优化处理,上线后效果明显,没有再出现频繁 OOM 的情况。

总结

大部分的 OOM 都是代码 bug,只有极少情况下是因为内存真的不够用,分析套路就是看内存里可疑的大对象是否是不应该出现的。