解決了一個java服務線程退出的問題

問題背景

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

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

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

初步定位問題

​ 查看代碼,發現調度線程在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超出限制,就會拋出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章