记录一次内存溢出

记录一次内存溢出

一.状况

线上出现了OutOfMemoryError

在这里插入图片描述

二.排查
  1. 最简单查看java内存的方法就是分析dump文件.
  2. 查找当前进程的Pid , pid 是 50480
  3. 到jdk安装目录bin下面找一个 jmap的命令
  4. 然后 ./jmap -dump:format=b,file=/opt/heap/heap1.bin 50480 , 得到 第一个 heap1.bin
  5. 过个把小时, 再使用这个命令 ./jmap -dump:format=b,file=/opt/heap/heap2.bin 50480 , 得到第二个heap2.bin
  6. 然后就是分析环节了, 我使用的是EclipseMAT插件, 具体安装过程百度之
  7. 用Eclipse 分别打开heap文件, 此时请看配图

在这里插入图片描述

在这里插入图片描述

当我们把两个, 都点击 Histogram 的时候, 会出现如下界面:

在这里插入图片描述

比较两个堆的生成情况,如下图

在这里插入图片描述

就是这些导致内存一路飙升, 此时的你应该能想到哪些地方使用到了FileInputStream了. 对, 没错, 就去那个地方找. 如果你对代码不熟悉的话, 也可以参照下面方法来定位位置:

  • 1.打开heap2文件

在这里插入图片描述

  • 2.生成表单

在这里插入图片描述

  • 3.在对象表单中寻找到我们刚刚看到的FiliInputStream, 然后右击选择List Objects, 然后选择 outGoing…

在这里插入图片描述

  • 4.然后你就可以看到这个FileInputStream到底是什么了, 这时候去代码那边找原因

我这里可以找到是如下图的这个类的这个方法:

在这里插入图片描述

三.原因分析

表面上看, 这段代码没什么大问题, 因为我的reader是需要在其他地方使用, 所以会在使用完之后关闭这个BufferedReader流. 然后嵌套的流也会相应的关闭. 没毛病啊.

的确BufferedReader对象在显示关闭的时候,会把嵌套的InputStreamReader流给关闭,

但是我们注意到上图红圈的地方,匿名new了一个FileInputStream对象,这个流是不会被关闭的,

同时因为 FileInputStream重写了finalize方法

在这里插入图片描述

**如果没有显式关闭流, jvm会有一个finalize()的方法来做最后的防线, 也就是说我们BufferedReader流虽然关闭了, 但是嵌套的流不关闭的话, 只能通过finalize()方法来关闭. **

但是在高并发情况下, 也许FileInputStream流开的很多, 但是finalize() 是单线程操作的,并且线程的优先级很低.finalize()方法会把需要释放的资源放到一个Queue中, 如果释放的动作慢于产生的速度, 这时就会有大量的Finalizer对象堆积, 没有及时释放资源,导致内存的异常.

四.解决方案

不使用匿名对象,将流依次显示关闭,问题解决

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值