记一次 JVM 线程 TIMED_WAITING 解决过程
问题描述
最近线上服务出现了大量的慢 sql,但是服务已经半个月没有部署过了,不应该是上新功能导致的。于是就直接查看机器的 jvm 监控,发现单台机器 TIMED_WAITING 状态的线程已经超过了 2k,其中一定有猫腻,那么问题大概就出现在这里了。拜托运维dump一台机器的线程具体信息继续进行排查
image.png
确定源头
通过日志分析,立马就找到了问题的源头,腾讯云存储 COS sdk 中调用了 wait 方法,导致线程处于 TIMED_WAITING,那么就通过这个 COS sdk 的源码入手。
image.png
IdleConnectionMonitorThread 该类用于监控空闲的线程池连接,但是代码看下来没有任何问题,每个 COSClient(用于操作 COS 的 client)只会初始化一个对应的监控线程,而且在 wait 之前会获取对象锁。既然代码没有任何问题,那么问题就只能是我用错了。
陷入迷茫,自我怀疑
那么再回过头来仔细梳理下自己的代码,同时重启服务,在服务重启一小时后 TIMED_WAITING 状态的线程梳理确实有增加。进一步验证自己的猜想,由于每次 Client 的初始化会耗费一定的资源,所以会提前申请好 Client,但是 Client 的配置存在变更的可能,于是又引入了延迟加载,每小时重新加载 Client,但是并未对老得 Client 进行释放,由此引发了这个问题。
解决问题,再升一级
问题的解决方案其实很简单,在申请新的 Client 时对老的资源进行释放即可,但是释放的时候存在资源还处于使用中的状态,于是引入了延迟释放,上线后进一步观察,TIMED_WAITING 稳定在 60左右。
其实整个问题也很简单,只不过自己用错了,没有对空闲资源进行释放。程序员的成长就是这样一个又一个的坑,吃一堑,长一智,基础知识最重要,三方库引入前至少要通读下接口文档。