1. 程式人生 > >openfire執行緒暴增導致堆溢位伺服器宕機排查處理

openfire執行緒暴增導致堆溢位伺服器宕機排查處理

軟體環境

openfire_src_4_0_2基礎上二次開發 、   jdk1.7.x、 visualVM1.3.8

問題現象

有20來個內部試用使用者進行訪問openfire,啟動一段時間後,客戶端的訪問體驗越來越卡頓。大約半天的時間後,openfire伺服器就報出堆溢位錯誤,無法再響應客戶端的請求。偶爾有一次報出了執行緒溢位異常:java.lang.OutOfMemoryError: unable to create new native thread。

原因分析

首先檢視服務端的日誌,堆溢位並不是固定在某段特定程式碼的記憶體造成的。所以進一步使用visualVM來監控記憶體和執行緒情況進行分析。

重啟openfire一段時間後監控到如下的記憶體和執行緒情況:


在伺服器中top出如下的資訊:


結合兩圖來看,堆記憶體只用了500多MB,加上非堆、永久代的記憶體量才700多MB,而查詢到的linux伺服器中的實體記憶體使用了1.4GB,多出來的700多MB是怎麼產生的呢?接著看執行緒數。一看嚇一跳,有7000多個執行緒存活。這就解釋了多出的700MB記憶體是怎麼來的,那就可以確定問題所在了,是由執行緒洩露所引起的。

接下來要要確定具體是由什麼執行緒所引起的,開啟jvisualvm面板,發現好多執行緒名稱都是一樣的(因為採用了java的執行緒池Executors的建立執行緒池方法,預設的執行緒池名稱是一樣的),所以要重新命名執行緒,暫時沒找到完美的解決辦法,但可以採用如下較為麻煩的辦法:

ThreadPoolExecutor中執行執行緒名稱的修改,實現想法:很簡單在具體的執行緒執行方法開頭中重新設定名稱。

這樣就可以使用visualvm很直觀地把執行緒執行情況一覽無餘,暴增的執行緒就很容易找到,同時通過執行緒的執行狀態,可以發現執行緒執行有效能上的潛在問題(比如:mina的業務執行是由IOHandler執行緒池的執行緒來處理的,若是有些執行緒處於執行狀態的時間太長,便可說明這個業務的處理可能有潛在的效能問題)。

最後找到了問題執行緒的實現程式碼,發現這段建立執行緒池的方法沒有使用單例,造成了一直建立執行緒池,導致執行緒暴增。

-----------------------------------------------------------------------------

附上經歷過的伺服器宕機問題的經驗總結

宕機因素:
1.資源不足(執行緒數、記憶體、檔案限制連結符數、執行緒數過多引起記憶體不足、磁碟空間不足);
2.執行緒異常(程式錯誤引起、輸入引數引起);


造成宕機最常見的程式異常
1.記憶體/執行緒等資源洩露;
2.多執行緒概率性錯誤;
3.資料庫效能問題(查詢慢、插入慢等)