一次因线程池使用不当造成生产事故的排查记录与思考

本文记录了一次由于线程池使用错误导致的生产事故,通过监控发现了JVM线程数量异常,分析线程状态发现大量线程处于WAITING(parking)。经过源码分析,找出问题在于线程池的局部变量未被回收,造成内存泄露。解决方案包括设置`allowCoreThreadTimeOut`或在方法结束时调用`shutdown`或`shutdownNow`。
摘要由CSDN通过智能技术生成

美好的一天从bug结束

某日当我点开熟悉的界面,一个又一个请求失败的提示赫然出现在屏幕上,不会是昨晚上线的代码有问题吧?

吓得我急忙按F12查看了响应——“exception”:“java.lang.OutOfMemoryError”,“message”:“unable to create new native thread”

出现了内存溢出的情况,无法创建更多的本地线程。

监控中显端倪

接着查看了实例的监控大盘,发现每个实例的JVM线程数量都处于9000+的规模,且还有上涨的趋势。

这是其中一个实例的线程数量监控图:

10.17日出现下降,并不是由于GC,而是由于上线,所以会降到0。

当前服务一共有8个实例,临时的方案是从负载均衡中摘除1个节点,用作排查使用,其他实例则按照顺序进行原地重建。

dump线程日志
登录上这个容器,使用

ps -ef|grep java

来查询到java应用的进程号,得到了1127

接着使用

jstack 1127 > thread.log

来导出进程内所有线程的状态数据,其中一部分的日志如图所示:

这两个线程,名称都是"pool-xxx-thread-xxx"的形式。

从Executors类中的静态内部类DefaultThreadFactory源码可知,默认线程工厂给线程池中的线程的取名规则就是这样的格式。

我们可以使用

grep -c "pool-" thread.log

来统计被线程池管理的线程总数,得到8336个,而一共才9000多个。

看来这次事故和线程池脱不了关系了

接着使用

grep java.lang.Thread.State thread.log | awk '{print $2$3$4$5}' | sort | uniq -c

按线程状态分组统计,得到这样的结果

40    RUNNABLE
5     TIMED_WAITING(onobjectmonitor)
63    TIMED_WAITING(parking)
12    TIMED_WAITING(sleeping)
73    WAITING(onobjectmonitor)
8100  WAITING(parking)

** RUNNABLE**
代表线程已经执行了start方法,但还没有获得CPU时间片,得到时间片后,才会变成RUNNING的状态。

**TIMED_WAITING(onobjectmonitor)与WAITING(onobjectmonitor) **

当线程获取监视器锁之后(一般是进入了synchronized方法),又调用了对象的wait或wait(time)方法࿰

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值