java 关于 Finalizer 过多导致内存(Res)缓慢上涨
2018-04-02 15:25
337 查看
病因: 事情的起因是由Flume的项目采集问题引发的. 测试人员发现用top命令查看采集进程的Res一直不断上涨姿势. 所以怀疑是内存泄漏.
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 大量堆积的情况, 异常解除.
由于本人是菜鸟一枚, 文中不免会出现这样那样的错误, 望大佬们提出宝贵建议, 十分感谢.
一, 对症下药
首先, 第一步肯定是先瞅瞅代码, 看看有没有那些资源啥的没关闭, 正如读者所想 ---- 没有发现.二, 通过辅助工具
最简单查看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 大量堆积的情况, 异常解除.
由于本人是菜鸟一枚, 文中不免会出现这样那样的错误, 望大佬们提出宝贵建议, 十分感谢.
相关文章推荐
- Cache占用过多内存导致系统内存不足最终java应用程序崩溃解决方案
- Linux服务器Cache占用过多内存导致系统内存不足最终java应用程序崩溃解决方案
- Linux服务器Cache占用过多内存导致系统内存不足最终java应用程序崩溃解决方案
- Linux服务器Cache占用过多内存导致系统内存不足最终java应用程序崩溃解决方案
- 关于Spark 1.5 版本中Spark自己管理内存而不是由Java管理内存的解释
- 关于java编辑器eclipse工具以及jdk安装导致的javac不能使用问题
- 关于java递归调用内存泄露
- 关于java内存分析
- java中使用堆外内存,关于内存回收需要注意的事和没有解决的遗留问题(等大神解答)
- java 虚拟机参数设置堆大小的分配可能导致内存暴增
- java频繁读取文件导致,内存不断增加
- [linux]服务器Cache占用过多内存导致系统内存不足问题的排查解决
- zabbix3.0.4关于java服务端程序内存溢出的处理
- 内存泄露和java.lang.ref.Finalizer
- 关于java处理内存泄露与内存溢出的学习总结
- 关于JAVA中的static方法、并发问题以及JAVA运行时内存模型
- 关于java中多态的理解,涉及到内存空间
- VB php JAVA关于数据库连接数过多的解决方法
- 关于Java占用内存的研究
- JAVA中关于字符串在内存的存储