记一次 JAVA 的内存泄露分析

 

目录

前言

正文

一、服务器上排查

二、本地解析

总结






前言

收到前端报错说应用无法访问,错误信息是Gc overhead limit exceeded,日志里有很多OOM,就开始了这一次内存泄露的排查之旅。

正文

一、服务器上排查

1、报错信息是:GC overhead limit exceeded根据这个信息来看,应该是有很多对象垃圾回收失败了。

2、首先看top,cpu 到达80%,说明一直在进行Gc回收,服务器内存还是足够的,说明不是服务器内存导致的。 top 按 c 进行排序后,发现占用cpu最高的是我们的应用。在根据日志里的报错提示,基本可以断定是出现内存泄露了,应用里出现对象没有被回收

3然后 jps 查看对应应用的 进程id,这次我们的进程id 是 16689

4、既然是内存泄露,那就应该看一下应用现在的内存情况,jmap -heap [进程id]发现了MinHeapFreeRatio 最小堆可用比例已经为0了,MaxHeapSize 是 520 MB,NewSize 新手区就已经用了519.5 MB ,老年区 OldSize 只有 0.5MB,堆实际使用情况看不到,出现了报错。根据上面的信息基本可以猜到,是创建了过多的对象,对象没有被回收成功导致的内存泄露

5、那就去看一下现在堆里的内存存活情况,看是哪个。 jmap -histo:live [进程id] > [文件名],之所以输出到文件里是因为对象的种类太多了,直接输出在控制台的话看不到前几名的,所以写入文件里,使用vim 查看 排名前几名。

可以发现排名第一名的是 char[] 数组,创建对象133万,占内存132 MB , 第二名是 LinkedHashMap ,创建对象 226 万,占内存 90 MB。这两个对象应用过于广泛,暂时定位不到问题,但第5,6名就有比较明确的方向了,是java 加密类的。也就是说这次内存泄露可能跟加解密有关。

不过范围还是有点太广,这个项目里加解密涉及到的地方比较多,只凭这个信息定位不到具体的类,还需要更详细一些的信息。

6、下载hprof文件到本地进行分析, jmap -dump:format=b,file=[文件名.hprof] [进程id] 下载后传输到本地电脑。

二、本地解析

1、在本地打开JProfiler 软件进行分析,这个软件可以跟idea 的插件配合,在idea 里打开对应类的源码查看。打开我们的hprof 文件。界面就是下面这个样子,可以看到实例数还是 LinkedHashMap数量最多,其次是 char[]。

2、我们先看LinkedHashMap,选中双击之后会出现新对象集,因为我们想看的是这个类是被哪个类所引用了,所以选择 【合并的传出引用】,往下翻,发现了一个53%来自于字符串,46%来自于 java.seceurity.Provider,跟我们预计的加解密导致比较相符,再往下翻发现 46%的99%来自于org.bouncycastle.jce.provider.BouncyCastleProvider,这个眼熟,去代码里查,发现有两个工具类里有这个类,但一个工具类没有被使用。再去查这个被使用的工具类的代码,找到问题了。

这个工具类里有静态方法里每次都会新建一个 BouncyCastleProvider 对象,新对象因为在静态方法里,所以一直没有被回收,修改代码为静态代码块初始这个对象,而不是每次都创建。

修改后再次测试和查看就没有再出现内存泄露了。

修改后:

 






总结

经验总结,在查资料的时候发现,jvm 可以设置一个 -XX:+HeapDumpOnOutOfMemoryError 参数在OOM时打印堆栈信息,这样可以更便于排查,我这次没有设置,就需要自己去生成日志了。
另外不要在静态方法里新增对象,这样不会被垃圾回收,最好是使用同一个对象。
还有测试环境里的堆内存可以调小一些,这样出现内存泄露的时候会比较容易被发现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值