unable to create new native thread问题定位
【错误信息】
java.lang.OutOfMemoryError: unable to create new native thread
【解决思路】
1、看到该异常后。首先,尝试打印堆栈(jstack PID),结果尽然打不出来,报如下错误
2、这下问题不好定位了。查看具体线程数是多少(top -H -p PID),尽然有4000多个
3、查看了该服务器上可以启动的线程数(ulimit -u),发现线上数为4096,当前出问题的进程就占用了4013。
4、以上操作,发现对于当前定位,意义并不大,具体问题出在了哪里,还是没有找出来。于是,尝试了另外打印堆栈的方式(kill -3 PID),堆栈信息打印在了catalina.out日志中,发现堆栈中尽然有3000多个Timer线程。
5、然后查看了业务逻辑中使用Timer的位置,会在任务不需调度时,通过Timer来定时调度。但是,每个任务就会起一个Timer,至于为啥会有这么多线程。查看了Timer的相关代码。
private void mainLoop() {
// 这里尽然是一个死循环 while (true) { try { TimerTask task; boolean taskFired; synchronized(queue) { // newTasksMayBeScheduled默认值时true, 同时无法更改 while (queue.isEmpty() && newTasksMayBeScheduled) queue.wait(); // 也就是在queue为空,比如走到这个位置,这就解释了堆栈中为啥会处于WAITING状态 if (queue.isEmpty()) break; // Queue is empty and will forever remain; die // Queue nonempty; look at first evt and do the right thing long currentTime, executionTime; task = queue.getMin(); synchronized(task.lock) { if (task.state == TimerTask.CANCELLED) { queue.removeMin(); continue; // No action required, poll queue again } currentTime = System.currentTimeMillis(); executionTime = task.nextExecutionTime; if (taskFired = (executionTime<=currentTime)) { if (task.period == 0) { // Non-repeating, remove queue.removeMin(); task.state = TimerTask.EXECUTED; } else { // Repeating task, reschedule queue.rescheduleMin( task.period<0 ? currentTime - task.period : executionTime + task.period); } } } if (!taskFired) // Task hasn't yet fired; wait queue.wait(executionTime - currentTime); } if (taskFired) // Task fired; run it, holding no locks task.run(); } catch(InterruptedException e) { } } }
6、基本逻辑出问题的点,基本理论上已经定位清楚。通过,按照理论推断,进行了复现。
7、修复的方式,是使用了共用的Timer(这里可能存在一个问题,就是内存抗不住,关于内存的问题,后续根据实际情况观察)