记一次线上问题-java内存溢出


这里分享一个线上问题案例及解决过程。
小k来到新公司不久,一天早上,小K收到一封报警邮件,提示某项目出现较多异常。小k点击邮件查看详情,发现提示out of menery错误。此时同事们还没来,小k暗自吐槽:“怎么都没来,这个项目代码自己也还不熟悉阿”。但是小k转念一想:“毕竟也是有2年开发经验的人,自己先排查下吧”
 
于是小k开始查看日志系统,发现报错基本集中在个别机器上。小k打开该项目的机器监控,发现有两台机器的内存使用率高于正常水平。进一步发现这两台机器的线程数量远高于其他机器以及此前的正常水平。
 
服务内存监控(此为正常情况,异常是超过90%)
线程监控(此为正常情况,异常机器的JMX thread数量有个梯度上升)
小k心中有了方向,进一步针对机器的线程收集监控,获取到异常机器的大量线程来自同一个线程池。此时小k的组长大M来了,小k把相关问题以及排查结果向大M进行了说明,大M找到项目中使用该线程池的地方,小k暗叹:“还是对项目熟悉定位问题比较快哇”。然后两个人一起看起了代码。不一会,就发现了问题,当然代码的创造者早已离开。
关键代码如下:

ExecutorService executor = Executors.newFixedThreadPool(size);

问题在于这里的size是会根据请求中某参数的数量动态创建的。原先的的请求内参数并没有特别多,创建了定长线程池执行完后可以安全释放。但是现在遇到两个参数过多的请求(并没有对请求方限制批量处理的的数量),导致创建过多线程消耗过多内存,程序异常,也无法执行下面的对线程池的回收和销毁。
 
确定了问题之后,组长让我对这里进行了修改,添加了创建线程数量的限制,防止线程泄露。上线后项目也恢复了正常。
 
小k在这次问题排查中学到了很多:

  1. 创建线程池本身要注意,要合理限制创建线程的最大数量,防止消耗过多的资源;
  2. 线程池使用中要确保在正常和异常情况下对线程池进行释放;
  3. 批量接口最好对最大请求数量进行约定和限制,防止不可控的异常;
    小k整理了下思路,继续开始一天愉快得工作,直到深夜….

文章作者: Xudong Jiang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Xudong Jiang !
 上一篇
java并发专题-线程池介绍 java并发专题-线程池介绍
今天介绍下java中的线程池应用,主要介绍java中的Excutor框架 线程池的好处 提高响应速度,处理任务时无需等待线程创建; 提高线程管理性,对线程池内线程统一分配、调度、管理; 降低资源消耗,通过重复利用已创建的线程降低线程创建和销
2019-12-08
下一篇 
java容器-列表和队列 java容器-列表和队列
今天介绍一些java内的容器-列表和队列,有些容器会在后续案例中更详细介绍 列表和队列 ArrayList 实现了Iterable接口,可迭代, 内部有一个数组elementData,一般有些预留空间,有整数size记录实际元素个数; 添加
2019-11-05
  目录