问题解决:记录一次Java程序内存泄露的解决过程(proxool内存泄露)
文章目录
问题场景
在线上运行的程序,有一天突然前端响应缓慢,但是后台日志依旧还在正常输出。针对这种情况,本篇博客主要是进行问题解决的过程说明。
问题环境
软件 | 版本 |
---|---|
Centos | 6.4 |
JDK | 1.6 |
proxool | 0.9.0RC3 |
问题原因
1. 使用top命令查看进程情况
2. 使用jstat命令查看当前GC情况
jstat -gcutil 13062 5000
从上图可以看出,现在项目是处于频繁GC的状态,内存基本都被占满了。
3. 使用jmap命令打印当前存活的最大20个对象
jmap -histo:live 13062 | head -20
从上图可以看到,目前是数据库相关的类占用了比较大的空间。其中有proxool,这个时候猜测是因为proxool的问题导致的。
4. 使用jmap保存当前堆栈信息
jmap -dump:format=b,file=heap-dump.bin 13062
5. 使用MAT工具进行head.dump的分析
将第4步导出的堆栈信息,使用MAT工具打开,打开之后,进行分析,分析结果如下:
从图中,可以知道,Finalizer占用了最大的空间,达到了2.9GB。
6. 使用jstack命令获取进程的堆栈信息
之后,我们使用以下命令将该进程的堆栈信息dump下来,如下:
jstack 13062 > 13062 _error.log
并搜索Finalizer字眼,结果如下:
7. 锁定问题
这个就是比较著名的proxool
内存泄露问题,在JVM
回收WrappedConnection
对象时,由于代理类重写了finalize
方法,WrappedConnection
方法被丢进引用队列等待finalizer
线程执行finalize
方法,finalize
本身没有额外的实现,但是代理类在执行该方法之前会做一个isClose
的判断,而jdbc oracle
的实现类则使用了synchronize
修饰了isClose
,导致业务逻辑从池里拿出来该连接使用的时候会与finalize
线程竞争该锁,一旦业务逻辑处于繁忙状态则finalizer
线程执行的频率大大减小,此时在队列中的引用依然存在,对象仍然会在堆中存活。
解决方案
既然知道了原因,那么覆写org.logicalcobwebs.proxool.WrappedConnection
类,添加以下代码:
然后重新编译提交到项目中,并重启。
结果
升级补丁之后,重启项目。到了第二天,再将堆栈信息dump下来查看,已经没有Finalizer的内存占用了。问题得到解决。
总结
本篇博文主要是记录此次解决过程中使用的各种命令,熟悉使用可以解决很多问题。
参考链接
解决proxool连接oracle内存溢出的问题
压测调优之遇到的proxool问题
随缘求赞
如果我的文章对大家产生了帮忙,可以在文章底部点个赞或者收藏;
如果有好的讨论,可以留言;
如果想继续查看我以后的文章,可以点击关注
可以扫描以下二维码,关注我的公众号:枫夜之求索阁,查看我最新的分享!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义