一、现象分析
上篇博客说到,Java服务假死的原因是使用了Guava缓存,30分钟的有效期导致Full GC无法回收内存。经过优化后,已经不再使用Guava缓存,实时查询数据。从短期效果来看,确实解决了无法回收内存的问题,但是服务运行几天后,发现内存又逐渐被占满,Full GC后只能回收一小部分。
从上图可以看出,一次Full GC后,老年代基本上没有回收多少内存,占比从99.86%降到99.70%。
二、原因排查
到底是什么对象占据这么大的内存,并且无法被JVM垃圾回收呢。在上一篇博客中已经移除了Guava缓存,按理说不应该有无法回收的对象了。那么,很明显这应该是代码问题导致了内存泄露,现在需要知道哪些对象无法被回收,从而定位出代码哪里有BUG。这里采用 jmap -histo:live 201349|head -10 命令打印出GC后存活的对象。
从上图可以看出,还是之前存在Guava缓存里面的对象占据着大部分内存,代码修改为实时查询后,每次用完数据都会从Map中剔除,按理不应该有强引用去引用这些对象。光看代码无法排查出哪里导致了内存泄露,只能将GC后的内存文件导出来进行分析。这里采用 jmap -dump:format=b,file=/