问题描述
最近有两台服务器经常出现此问题。 操作系统分别为centos7.9 和 centos6.10, jdk1.7.0_80, tomcat8.5。
现象为: cpu负载降低但还是有一定负载, 带宽使用明显下降, jvm并没有卡死,还是有部分业务能执行, 堆内存使用速度明显变慢, gc频率降低。
通过关联的线程栈分析,可以看到有370个线程在等待 java.util.IdentityHashMap
这把锁。 但是没有线程持有些锁。
需要说明的是:这几次发生此现象的时候锁的地方不固定。 有时候是hibernate的getCache, 有时候是SSL相关的,还有其它的。
我的认知当中,正常情况下,至少会有一个线程持人锁, 才会导致其它线程等待, 这种无人持有的情况完全超出了理解范围。
这里不知道怎么上传线程dump文件, 我上传到其它地方了:线程栈详情
问题出现的环境背景及自己尝试过哪些方法
机器配置: 阿里云ECS。 8核32G。
运行环境: centos7.9, centos6.10, jdk1.7.0_80, tomcat8.5
jvm 选项: -server -Xms12g -Xmx12g -XX:PermSize=256m -XX:MaxPermSize=256m -Djava.library.path=/usr/local/apr/lib
应用主要是跑定时任务的,有很多http请求,还有大量图片处理操作,因为调用外网api和图片很多, 所以线程耗时都比较长,FullGC 一般10分钟左右就会进行一次,一次耗200ms左右的时间。
相关代码
同步块代码是Druid连接池的com.alibaba.druid.pool.DruidDataSource.getConnectionDirect方法, 同步块处代码为:
if (isRemoveAbandoned()) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
poolableConnection.setConnectStackTrace(stackTrace);
poolableConnection.setConnectedTimeNano();
poolableConnection.setTraceEnable(true);
synchronized (activeConnections) {
activeConnections.put(poolableConnection, PRESENT);
}
}
你期待的结果是什么?实际看到的错误信息又是什么?
希望大家帮我分析下出现这种情况的原因。
这可能跟锁没有关系,我看了ThreadPoolTaskExecutor这个线程池,其中有一部分TIMED_WAIT状态的线程,stacktrace中有这样的信息:
at
java.util.concurrent.FutureTask.get(FutureTask.java:199 )
146
at
com.tb.util.thread.ThreadPoolTaskExecutorUtils.executeSubmit(ThreadPoolTaskExecutorUtils.java:94 )
146
at
com.tb.util.thread.ThreadPoolTaskExecutorUtils.submit(ThreadPoolTaskExecutorUtils.java:70 )
146
我推测这块的代码是当线程池中的某个线程在执行过程中,需要一个子线程来做异步,主线程等待子线程执行结果后再继续执行,但这个子线程任务却也提交给了同一个线程池。这样用线程池是有问题的,因为在线程池没有空闲线程时任务会先提交的队列中,等待有空闲线程再去队列中取任务执行。放在这个场景里,就是主线像线程池中提交一个子任务进入队列排队,然后主线程在子任务完成前一直阻塞,而子任务则在线程池队列中等待主线程执行完成,因此形成了死锁的现象。你可以排查一下是否存在此问题,如果是这个问题那么验证也是很容易的,只要不断增加并发量,达到一定程度就一定会出问题