记一次因烂代码引起的 OOM 事件原创
本文正在参加「Java应用线上问题排查经验/工具分享」活动
背景
某天,公司某内部财务相关系统的研发找到我,说他们的系统内存只增不减,jmap 也看了没什么问题,弄的他们现在只能每几天重启一次避免服务挂掉……
问题描述
听起来像是内存泄露了,排查这种问题嘛,还是老一套,先看监控。
从 top 上看,该进程占用了 6G内存,可 jmap 的结果里,heap + noheap 一共也才不到 4G,难道还有 mmap/direct 之类的堆外内存?
然后通过 Arthas ,看了下堆外内存,也没有什么异常,数值很低……
这就有点奇怪了,JVM 实例所有内存(capacity)加起来也没这么多,那多余的 2G 内存哪去了呢?
而且这个研发还说,只要不重启服务,内存会一直增加,永远不会降低……
结合他描述的情况,很明显是内存泄露了,不过奇怪的是 heap + noheap 的总和加起来也没这么大,mmap/direct 也很低……内存去哪了呢?
此时我有点开始质疑自己了,难不成有什么更深层次的内存问题?就算是Netty 那块泄露了,也应该是 Direct 部分啊,多出的内存到底是哪来的……
冷静了几分钟之后,才反应过来。由于是内存问题,刚才看的一直是内存相关的指标,其他指标也得关注一下,毕竟这些指标都是有关联的。
于是我又看了眼该进程的线程情况,不看不知道,一看吓一跳……
这个服务进程竟然有好几千个线程……其中有 80% 都是 WAITING 状态
好了,这下应该是找到原因了,这么多线程,还都没在执行,内存不炸才奇怪呢。多半是滥用线程池,或者是手动创建线程,然后线程一直阻塞……
然后我指着这个数字,对研发小哥说:“你看这个接近 5 位数的线程总数,它美嘛?”
研发小哥看到这个数字之后,满脸尴尬的说:“我的错,我的错……”
我:“行吧,这明显是你们这个系统里线程用法不对,去检查一下所有使用线程池和手动创建线程的地方,看看有没有什么疑点,比如每次 new 线程池,用完不 shutdown 之类的……”
过了大概俩小时,这小哥回来了,说:“哥我找着了,跟你说的情况一样!有个菜鸡写的代码,每次 new 一个线程池去跑线程,用完还不 shutdown !”
“这个菜鸡是不是你?”
至此,问题解决……
总结
对于一些老系统里的屎山代码,出问题很正常,但凡一点变动就可能引起一些 Bug,而且由于代码过老,使用频率低,一般人还不敢动。
只能是遇一个坑,填一个坑,屎山代码可不是一般人能碰的了的东西……