起因
我负责的APP项目系统,简称A系统。在与其它项目T系统不在同一个服务器的情况下,发现T系统重启会导致A系统并发量瞬间暴增,最后导致A系统内存溢出挂掉。
排查
1、服务器日志排查
通过XShell6工具查看系统服务日志,下载日志并排查在服务器宕机的时间内的异常,
异常信息中没有具体哪个接口、方法导致,于是找运维要了heapdump文件,利用JDK带的jvisualvm工具进行分析
2、通过工具分析
工具位置,在JDK中的bin目录下,工具的具体使用请自行查阅
将heapdump文件导入分析:
通过工具定位的两个接口。
3、接口压测
将定位到的接口交给测试进行压测,压测的结果的是其中一个接口在并发量50就出问题,进行优化再上线。
4、等待T系统发布版本
发布版本后发现A系统还是挂掉
5、统计A系统宕机区间的接口访问量
统计命令
sed -n ‘/2021-12-22 21:27:01/,/2021-12-22 21:29:47/p’ catalina.out|grep 第三方获取工单详情|wc -l
发现访问量在那个时候还是暴增,于是询问T系统使用接口的业务情况以及排查,发现他们的中间件使用异常,无法进行正常的ack机制,于是在重启的瞬间将积累的访问都重新调用了一遍。
6 最后
1、优化了接口,压测结果:1000并发量没有出现异常
2、T系统修改了BUG
3、运维修改了配置
#net.ipv4.tcp_tw_recycle = 1 将这个注释了
net.ipv4.tcp_keepalive_time = 500
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
后面T系统重启两次,APP项目无异常,准备后续T系统再发版本重启系统,监测下情况。待续
后续系统还是偶尔会内存溢出,Java VisualVM只提供了一些基本的功能,无法具体定位问题。
要使用Eclipse Memory Analyzer来分析。
排查的结果是第三方重启时还是会多次调用一个接口,偶尔会不传参进行调用,导致接口查询了全表数据,数据量太大导致内存溢出
Eclipse Memory Analyzer下载地址:
https://www.eclipse.org/mat/downloads.php
排查过程
拿到dump文件
修改mat内存
因为dump文件超过1G,而mat初始大小为1024,不修改会打不开文件
打开文件
总共使用的内存为1.2G
Thread对象占用了755.2M
点击Leak Suspects查看具体的内存泄露报告
DETAILS
点击圈起来的,查看引用关系,下图,可以很清晰的看到是由于ArrayList中放了18W个byte数组造成的
回到details,点击选择Java basics-》Thread Details
通过线程中可能引起内存溢出时代码的执行位置,一一排查,最后定位
最后发现因为第三方参数未传必传参数id,而该方法也没有进行必填参数校验,导致该方法查询出全表数据(全表数据有18W多),这也是ArrayList中有18W个byte数组的原因。最终导致了内存溢出。
再次确认
通过本地配置和线上一样的内存-Xms1536m -Xmx1536m
再进行本地调用该方法并且不传参数,在第二次调用发生了线上一样的内存溢出报错
解决
对该方法进行必填参数校验,上线后没有再次发生内存溢出的问题