JAVA8-线上内存泄漏分析

环境:CentOS7+JAVA8+SpringBoot

启动内存配置参数:-Xmx6g -Xms6g -Xmn2g -Xss1m

收到监控报警服务响应时间>400ms,监控内存占用居高不下>99%

首先进机器

jmap -heap 2860 

发现持久代空间占用>99.8%

jstat -gc 2860 250 6

发现非常多的Full GC,但是并没回收多少内存,初步判定有内存泄漏的问题

常用方案jvisualvm直接连RMI监控...但是线上环境哇,内存动不动就5-6个G,dump内存数据也这么多,要这样做那么动不动线上就瘫了....

既然是线上,按流程来走....

首先切量,把一台机器的访问量全部切走....方法很多一般 nginx走权重配置,阿里云的SLB也可以

当机器没什么访问量的时候,开始动手

jmap -dump:format=b,file=/data/2860.dump.dat 2860

jmap -dump:live,format=b,file=/data/2860.live.dump.dat 2860

dump:live的作用是会触发Full GC,然后在dump数据,所以留个前后的数据做对比

这样我2860.live.dump.dat的文件发现有5G,这个下载下来用MAT分析....也得很久啊,当然可以走百度,一个晚上应该可以了...但这并不是我想要的

而且我用百度弄过一个1.5G的文件下来分析过,实际上MAT并没带来多少好处对于我这个问题(业务导致的内存泄漏)

解决大文件分析最好的方法就是不下载直接在机器上分析,还好找到一个工具jhat(传说java9以后移除了,不过可以自己下载)

jhat -J-Xmx6g -port 8888 /data/2860.live.dump.dat &

等个几分钟,用http://服务器IP:8888访问

别看一大堆东西,有用的就最后一点点,拖到页面最后


JAVA8-线上内存泄漏分析_第1张图片

看下面的图,找一下自己写的比较熟悉的类,从上开始,数量越多,内存泄漏的风险越大,别看DefaultHandle 这么多(当然也有比较大的风险),一般这种不是自己直接写的,就算内存泄漏也是因为自己写的代码引起的,先解决自己熟悉的,然后再处理其它的(之前也看到过因为netty版本导致的内存泄漏)

JAVA8-线上内存泄漏分析_第2张图片

正常来讲既然量全部切走了,Full gc后Packet 应该为0才对,但是这里居然还有?那么肯定有地方引用,点进去看看


JAVA8-线上内存泄漏分析_第3张图片
JAVA8-线上内存泄漏分析_第4张图片

进去一看,崩溃啊,一大堆的node,ConcurrentLinkedQueue$Node,根本就找不到直接引用的地方。没办法结合代码来看哪些地方引用了Packet,确实是有个LinkedQueue来装Packet,但是连接关闭的时候 整个数据链应该也消失才对,但是为啥没呢?难道要手动清,不过手动清有可能导致数据包丢了,啥时候触发手动清理?按正常逻辑分析,Packet对应的调用链肯定存在活着的引用,还是从代码开始,一层层分析引用链...

这里线上环境用以上方法就是要找到内存泄漏的风险最大的位置,找到这个位置后,可以在本地或者测试环境用jvisualvm连接上来查找Packet引用链路,这个是最快的

如下图


JAVA8-线上内存泄漏分析_第5张图片

当然直接看代码分析也是可以的。只是比较漫长....而且要有敏锐的...

按照引用链路分析,最后发现问题了...这个问题隐藏的很深...具体就不提了...netty-socketio的

发现ClientHead里面每次新建连接都会new ClientHead,不管sessionid相同还是不相同,而ClientHead里面挂了引用Packet的链路,这就有问题了啊...sessionid相同的情况下 再连接一次,上一个ClientHead咋办?没关闭....不成浪子了嘛?ClientHead会一直接受下发的消息,这样内存就被这个浪子消耗光了....最简单的,sessionid相同 查到有ClientHead直接将其关闭...

打包上线....重复以上操作观察...停量以后ClientHead和Packet数量都为0了...这就对了撒

再次Full gc基本上能回收50%的老年代

你可能感兴趣的:(JAVA8-线上内存泄漏分析)