java 关于 Finalizer 过多导致内存(Res)缓慢上涨

发布时间 2023-11-24 16:46:45作者: 沧海一滴

 

病因: 事情的起因是由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