java 关于 Finalizer 过多导致内存(Res)缓慢上涨
病因: 事情的起因是由Flume的项目采集问题引发的. 测试人员发现用top命令查看采集进程的Res一直不断上涨姿势. 所以怀疑是内存泄漏.
一, 对症下药
首先, 第一步肯定是先瞅瞅代码, 看看有没有那些资源啥的没关闭, 正如读者所想 ---- 没有发现.
二, 通过辅助工具
最简单查看java内存的方法就是分析dump文件.
1> 查找当前进程的Pid , 如图所示, pid 是 50480
2> 到jdk安装目录bin下面找一个 jmap的命令
3> 然后 ./jmap -dump:format=b,file=/opt/heap/heap1.bin 50480 , 得到 第一个 heap1.bin
4> 过个把小时, 再使用这个命令 ./jmap -dump:format=b,file=/opt/heap/heap2.bin 50480 , 得到第二个heap2.bin
5> 然后就是分析环节了, 我使用的是Eclipse的MAT插件, 具体安装过程百度之
6> 用Eclipse 分别打开heap文件, 此时请看配图
当我们把两个, 都点击 Histogram 的时候, 会出现如下界面:
根据图中所示, 选择对比两个堆文件变量的生成情况, 然后会得到下图:
快看快看, 就是这龟儿子导致了我们的内存一路飙升, 此时的你应该能想到哪些地方使用到了FileInputStream了. 对, 没错, 就去那个地方找. 如果你对代码不熟悉的话, 也可以参照下面方法来定位位置:
(1) 如图, 我们再次点击这个, 然后得到变量生成表单.
(2) 在对象表单中寻找到我们刚刚看到的龟儿子FiliInputStream, 然后右击选择List Objects, 然后选择 outGoing...
(3) 然后你就可以看到这个FileInputStream到底是什么了, 这时候去代码那边找原因.
7> 由于涉及到代码保密协议, 我就用测试代码替代源代码, 大致逻辑如下
表面上看, 这段代码没什么大问题, 因为我的reader是需要在其他地方使用, 所以会在使用完之后关闭这个BufferedReader流. 然后嵌套的流也会相应的关闭. 没毛病啊.
也许这是所有程序猿公认的, BufferedReader 流关闭, 嵌套的流也会相应的关闭. 这句话没有错. 但是, 在高速度和高并发的情况下, 对于流的关闭就会有问题.
因为 如果没有显式关闭流, jvm会有一个finalize()的方法来做最后的防线, 也就是说我们BufferedReader流虽然关闭了, 但是嵌套的流不关闭的话, 只能通过finalize()方法来关闭.
但是在高并发情况下, 也许FileInputStream流开的很多, 但是finalize() 是单线程操作的(具体内部原理请百度之).finalize()方法会把需要释放的资源放到一个Queue中, 如果释放的动作慢于产生的速度, 这时就会有大量的Finalizer堆积, 导致内存的异常.
所以, 建议在高并发, 高速度的条件下, 尽量别使用嵌套的BufferedReader流. 改用下面:
通过反复测试, 没有出现Finalizer 大量堆积的情况, 异常解除.
由于本人是菜鸟一枚, 文中不免会出现这样那样的错误, 望大佬们提出宝贵建议, 十分感谢.
————————————————
版权声明:本文为CSDN博主「Hello法克」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41771503/article/details/79789086
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
2015-11-24 Arrays.asList的那点事