問題背景
早上才上班,測試就提了一個問題:"昨天所有批量任務都沒有跑"。我看了一下任務監控頁面,任務是有生成的,但卻一直在等待排程狀態。初步懷疑是我們的排程服務問題,於是上去檢視排程服務日誌。
從日誌上觀察,發現沒有排程日誌。正常情況下即使沒有任務,也會有日誌輸出,說明沒有任務需要排程。於是懷疑排程執行緒沒啟動。
說明一下背景:我們的排程服務主要由排程執行緒和任務執行緒組成,排程執行緒定時掃描任務表,發現有任務需要排程就啟動任務執行緒處理。
初步定位問題
檢視程式碼,發現排程執行緒在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超出限制,就會丟擲