【錯誤信息】
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(這里可能存在一個問題,就是內存抗不住,關於內存的問題,后續根據實際情況觀察)