首先说明下我这个标题可能起的不到位,其实我本次要介绍的是一次生产定位问题的思路及过程。
国庆前期发布了一个很小版本,大家都以为没什么问题,可是发布后生产出现了问题并且持续了两个小时以上,现象如下:
ERROR|org.hibernate.engine.jdbc.spi.SqlExceptionHelper|[SimpleAsyncTaskExecutor-52] Timeout: Pool empty. Unable to fetch a connection in 30 seconds, none available[size:150; busy:150; idle:0; lastwait:60000].
最直接报错就是生产应用程序获取数据库连接无法正常获取,当时未进行定位问题的情况下只打印了线程栈快照,堆快照信息未保存,然后扩大数据库连接,最后重启,重启后无问题(当晚未留,第天定位)。
这里涉及到信息安全问题,公司内网无法发送资料,所以本地再现了下,线程栈信息大致如下:
1.复现HashMap死循环
这里我参考了
代码如下:
package com.spring.test.deadmap;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class HashMapThread extends Thread {
private static AtomicInteger ai = new AtomicInteger(0);
private static Map<Integer, Integer> map = new HashMap<Integer, Integer>(1);
public void run() {
while (ai.get() < 100000) {
map.put(ai.get(), ai.get());
ai.incrementAndGet();
}
}
}
package com.spring.test.deadmap;
public class Test {
public static void main(String[] args) {
HashMapThread hmt0 = new HashMapThread();
HashMapThread hmt1 = new HashMapThread();
HashMapThread hmt2 = new HashMapThread();
HashMapThread hmt3 = new HashMapThread();
HashMapThread hmt4 = new HashMapThread();
hmt0.start();
hmt1.start();
hmt2.start();
hmt3.start();
hmt4.start();
}
}
使用低版本jdk较容易复现死循环,多试几次。
2.证明线程无法结束是因为HashMap存在环
我们需要借助堆快照形成堆内实例dump文件,借助MAT进行分析当前HashMap.Entry[]数组中的Entry链表是否存在循环引用