记一次 Java Web 服务申请僵死的状况,起因是因为申请线程池死锁占满,无奈接管新的申请。以下为这次问题的排查过程:
1、网站告警某个实例无奈响应,找到对应的实例,手动 curl
申请接口,无奈响应。
2、应用 jps
命令找到对应过程的过程号。
3、应用 jstat
查看 GC 是否还失常执行,残缺命令:jstat -gcutil 1624 1s
,每隔 1s 打印一次内存及 gc 状况,如下图:
从上图看出基本上内存没有啥变动,也不现执行 GC,一方面阐明过程基本上没有执行啥工作;另一方面也能够反映 JVM 并没有齐全僵死,还在存活(内存有大量的变动)。再看一张前面执行的 gc 状况,如下图:
基本上也能看出进行了一次 GC,列 YGC 次数比上一张图片减少了一次。
4、再应用 jmap -heap 1647
确认 JVM 内存并没有压力,如下图:
5、查看一下线程的状况 jstack -l 1647 > jstack-20201224.log
, 并将后果输入到文件中,不便排查。关上文件发现有大量以 qtp 为结尾命名的线程,统计一下,如下图:
经查,因为我的项目应用的是 jetty 容器,申请线程池中的线程命名就是这样的命名,很显著,线程被占满了,并且不开释。再看一下失常的 JVM 实例是什么样的?
线程数才 28 个。再确认下异样线程的状态:
6、关上方才导出的查看线程的文件,找下具体什么起因导致线程无奈开释,如下图:
能够看到是因为线程阻塞在 BasicFuture.get()
办法处,进而看到本人的代码调用地位位于 ProcessTaskQueryResultService.java
的 198 行,如下图:
能够看到代码应用了 Apache 的 httpasyncclient 库的异步调用,返回 Future,如果申请异样,在此处就是申请了一个比拟大的响应后果,接口比拟消耗资源,导致无奈响应,将始终阻塞在 Future.get()
处,最好的方法就是设置获取后果的超时工夫,如上图改后的示例。