問題背景

​ 早上才上班,測試就提了一個問題:"昨天所有批量任務都沒有跑"。我看了一下任務監控頁面,任務是有生成的,但卻一直在等待排程狀態。初步懷疑是我們的排程服務問題,於是上去檢視排程服務日誌。

​ 從日誌上觀察,發現沒有排程日誌。正常情況下即使沒有任務,也會有日誌輸出,說明沒有任務需要排程。於是懷疑排程執行緒沒啟動。

​ 說明一下背景:我們的排程服務主要由排程執行緒和任務執行緒組成,排程執行緒定時掃描任務表,發現有任務需要排程就啟動任務執行緒處理。

初步定位問題

​ 檢視程式碼,發現排程執行緒在spring bean初始化時啟動的。 沒有任何分支,理論上應該不可能沒啟動。

​ 回去檢視日誌,發現排程服務啟動時,排程執行緒是有正常啟動的。而且之後排程很正常,一直到昨天下午五點多之後突然就沒有排程執行緒的日誌了。

​ 這裡突然想起來,昨天下午五點多的時候,測試做了壓測,結果導致伺服器都登入不上。(登入時提示:“fork: retry:資源不可用”)

​ 後來使用root登入後殺掉一些程序,才恢復正常。(linux會給root使用者保留一些資源,方便管理員處理系統故障)

​ 當時檢查,發現原來是最大程序數設定成1024,但我們伺服器上部署了幾個java服務,每個服務又開了很多執行緒。壓測時執行緒數上升,導致系統資源耗盡。

​ 於是初步懷疑是當時資源耗盡引起排程執行緒的問題,但具體是怎麼引起的,還需要再進一步確認。

確認問題

​ 先嚐試使用jstack檢視排程服務執行緒,想看一下是不是排程執行緒因為什麼鎖卡住了。輸出結果發現一個奇怪的情況:排程執行緒不見了。

於是猜測:難道是昨天下午壓測時資源不足,引起了排程執行緒出異常?(因為排程執行緒需要開啟任務執行緒,任務執行緒因為當時系統資源不足,肯定開啟失敗)

​ 上網搜尋了一下,發現其他人也碰上過類似情況。解決方法是增加Catch Throwable,這樣可以防止執行緒退出。

​ 考慮到Catch Throwable可能導致虛擬機器一些異常無法恢復,影響後續功能。我直接列印了日誌退出。

問題復現與複測以及防止

​ 由於比較忙,沒做復現。修改了程式碼讓測試重新壓測一下,發現問題沒再發生。到此告一段落。

這次學習到了幾個知識點:

1 java服務裡面執行緒崩潰不會引起程序退出,這跟我以前寫c++的經驗是不一樣的。

2 java的Error不是Exception的子類,只是捕獲Exception不能防止執行緒因為出現OOM而退出

3 OOM包括了好多種情況,無法建立執行緒是其中一種。全部情況如下:

java.lang.OutOfMemoryError:Javaheap space

堆記憶體(Heap Space)沒有足夠空間存放新建立的物件

Java 程序花費 98% 以上的時間執行 GC,但只恢復了不到 2% 的記憶體,且該動作連續重複了 5 次

永久代(Permanent Generation)已用滿,通常是因為載入的 class 數目太多或體積太大

Metaspace 已被用滿

JVM 向底層作業系統請求建立一個新的 native 執行緒時,如果沒有足夠的資源分配

所有可用的虛擬記憶體已被耗盡

作業系統OOM Killer關閉程序

程式請求建立的陣列超過最大長度限制

Direct ByteBuffer超出限制,就會丟擲