天天看點

問題分析:java.lang.OutOfMemoryError:unable to create new native thread

背景

背景系統抛出OOM錯誤,操作無法正常進行。調用背景日志,發現錯誤資訊為:java.lang.OutOfMemoryError:unable to create new native thread

分析

1、一般OOM比較常見的原因是堆記憶體不夠或者持久代記憶體不夠導緻,但這個問題是因為無法建立本地線程。 2、最初懷疑JVM的堆記憶體或持久代記憶體耗盡了,使用jmap檢視JVM記憶體資訊,發現一切正常。(有可能jmap等指令根本無法執行。) 3、通過free、top指令檢視系統記憶體,發現系統實體記憶體耗盡了。使用jmap指令将JVM的資訊dump到檔案,使用JVisualVM分析,發現有大量的線程處于等待狀态,并且這些線程都是ThreadPool。通過執行下面指令,可以獲知該程序下的線程數:

ps -L -p [pid] | wc -l
           

發現線程數量有幾千個,線程數量沒有達到作業系統限制的數量,但是記憶體不夠用了。 4、根據獲得的線索,懷疑代碼裡對線程池沒有正确使用。分析代碼後發現,線程池對象沒有使用static辨別,導緻每次在new一個新的對象時就會建立一個線程池。由于使用的是50個線程的固定線程池,每次任務運作完後,線程并不會關閉,而是處于等待狀态。是以,這樣一樣,調用次數過多,就會導緻建立很多個線程,直至OOM。

結論

1、其實分析中的第二步沒有必要進行。分析JVM的記憶體區域:堆記憶體、持久代、虛拟機棧、本地方法棧、Direct記憶體。我們用jmap隻能檢視堆記憶體和持久代記憶體。線程直接使用的是系統實體記憶體,用jmap無法看出來。 2、我們在配置-Xms、-Xmx參數時,雖然指定了堆記憶體的大小,但是JVM在啟動時,并不會一下申請這麼多記憶體。而是在建立新的對象時,找作業系統申請記憶體。同時,在GC後,JVM并不會将申請下來的記憶體傳回給作業系統,而是由它自身來進行管理。 3、堆記憶體配置過大,JVM可用的剩餘實體記憶體就會少。是以,需要綜合考慮系統的情況,合理配置設定JVM的堆記憶體,不然就容易出現各類OOM了。本次遇到的這個問題就是因為配置有問題:作業系統共有4G的記憶體,結果堆記憶體就設定了3G,如果JVM把3G的記憶體吃完了,也就剩下1G的記憶體供線程、緩沖區等其他使用者使用,是以明顯容易出問題。