内存泄漏也是一个老八股文了,下面来看看实际项目中内存泄漏的场景分析
时间回到9月某一天
现象:在当时各种请求在那段时间响应很慢,特别是 kafka异步消费线程
不足点:当时主业务基本不可用,有点急,未及时dump(当时大家没往GC那方面想,单纯以为流量大消费不过来)
第二天系统拉了日志也还未分析出来,只是临时增加了kafka消费线程的数量,单机从2扩到10,两台机器共计20消费线程,服务都重启了一遍
又过去了几天,来到了节假日流量高峰期
leader在群里发了几个阿里云告警信息,内存告警(这个时候已经距离上次程序重启好几天了)
dba也在群里发了异常信息,有发截图(截图中大量连接状态为 CLOSE_WAIT 以及 重复ip:121.51.50.140和121.51.58.151):句柄数一直在涨
注:当时我回复dba的话:句柄增加,在我印象里,业务代码里应该没什么本地IO操作,网络IO的话主要是我们服务之间会频繁的进行远程调用(-_-害 把订单模块给漏了,这里会去调阿里/腾讯进行支付)
当时在群里询问小伙伴有没有可以 根据句柄反推请求的方法,却把截图中的两个关键信息给漏了,还是经验不足 -_-
负责订单的同事上线了,发现两个突破点:
1、大量的连接状态是 CLOSE_WAIT
如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程 序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直 被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。
所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码,因为问题出在服务器程序里头啊
2、重复ip:121.51.50.140和121.51.58.151 (此为腾讯云服务器)
121.51.58.151:https (CLOSE_WAIT)
121.51.58.151:https (CLOSE_WAIT)
121.51.50.140:https (CLOSE_WAIT)
为什么有大量的连接处在CLOSE_WAIT状态? (qq.com)
TCP通信过程中time_wait和close_wait产生过多的原因和解决方法_time_wait连接过多的原因-CSDN博客
同时根据这两点定位到代码:
1、获取支付渠道配置(微信支付 微信支付分....)
2、根据获取到的配置判断httpClientMap中是否存在(会对比版本号),不存在则创建一个WechatPayHttpClient(或支付宝 或其他渠道)然后写入httpClientMap
乍一眼看流程没啥问题,其实在第一步每次都会获取一个“新”的渠道配置(版本号不一致),因渠道配置缓存的set get使用的key不同,等同于没缓存(这个缓存为本地hashmap,value为连接对象)
没缓存所导致的问题就是 每次支付都会重新build一个新的连接去调用支付渠道进行支付,然后每次都会将这个连接放入map中,此时内存就会随着支付的请求数量而持续增高,并且连接对象也不会被释放掉。在修复之前也通过dump文件看到最大的对象即是这个存储连接对象的map,最终展现的情况就是一直Full GC,所有业务请求在那段时间响应非常慢,连一个最基本的主键查询都很慢-_-
写到最后:此文是一位优秀且帅气的同事所排查分析,作为小菜鸡的我只能在此案例中默默学习学习๐·°(৹˃̵﹏˂̵৹)°·๐