InternalResourceViewResolver引起的内存泄漏问题排查
上午突然收到某项目服务器load,GC报警,通过监控发现FullGC频繁,遂登录服务器排查问题。
首先看到GC日志如下:
|
|
分析GC日志可以看到,每次FullGC时,年轻代Eden区得到有效释放,然而old区始终无法释放出足够的空间,剩余空间很小,一直接近满的空间。
可以得出结论,年轻代Eden区满触发GC,此时old区剩余内存空间小于历次youngGC晋升到old区大小,直接触发FullGC,FullGC后,内存并没有得到有效释放,所以判断此时发生内存泄漏,怀疑有资源未能释放。
然后通过 jmap -dump:format=b,file=core.bin pid
命令dump内存快照,下载到本地通过mat工具分析内存泄漏及相关引用链得到如下图:
发现org.springframework.web.servlet.view.InternalResourceViewResolver中的viewCache这个HashMap有50万个entries,占了700多M内存,从而导致内存泄漏。查看项目使用的spring版本3.1.2版本中的InternalResourceViewResolver源码,发现其继承自AbstractCachingViewResolver,这个类内部使用了一个HashMap做为缓存,但并没有有效的缓存释放操作,导致程序在返回ModelAndView的时候,只要ModelAndView的viewName不同,便会不断的往这个HashMap添加数据,最终导致内存泄漏。
3.1.2版本源代码如下:
|
|
只要返回的viewName不同那么每次都创建一个新的View对象然后添加到viewCache中。
3.1.4版本做出了修复,源代码如下:
|
|
新版本将HashMap换成了LinkedHashMap,通过LinkedHashMap实现一个LRU缓存,提供了缓存清理方式,防止内存泄漏。
我们可以通过升级spring版本到3.1.4及以上,或者关闭InternalResourceViewResolver的cache功能,修复该问题。