OutOfMemoryError系列(5): Unable to create new native thread

這是本系列的第五篇文章, 相關文章列表:

Java程序本質上是多線程的, 可以同時執行多項任務。 類似於在播放視頻的時候, 可以拖放窗口中的內容, 卻不需要暫停視頻播放, 即便是物理機上只有一個CPU。

線程(thread)可以看作是幹活的工人(workers)。 如果只有一個工人, 在同一時間就只能執行一項任務. 假若有很多工人, 那麼就可以同時執行多項任務。

和現實世界類似, JVM中的線程也需要內存空間來執行自己的任務. 如果線程數量太多, 就會引入新的問題:

java-lang-outofmemoryerror-unable-to-create-new-native-thread

java.lang.OutOfMemoryError: Unable to create new native thread 錯誤表達的意思是: 程序創建的線程數量已達到上限值

原因分析

JVM向操作系統申請創建新的 native thread(原生線程)時, 就有可能會碰到 java.lang.OutOfMemoryError: Unable to create new native thread 錯誤. 如果底層操作系統創建新的 native thread 失敗, JVM就會拋出相應的OutOfMemoryError. 原生線程的數量受到具體環境的限制, 通過一些測試用例可以找出這些限制, 請參考下文的示例. 但總體來說, 導致 java.lang.OutOfMemoryError: Unable to create new native thread 錯誤的場景大多經歷以下這些階段:

  1. Java程序向JVM請求創建一個新的Java線程;

  2. JVM本地代碼(native code)代理該請求, 嘗試創建一個操作系統級別的 native thread(原生線程);

  3. 操作系統嘗試創建一個新的native thread, 需要同時分配一些內存給該線程;

  4. 如果操作系統的虛擬內存已耗盡, 或者是受到32位進程的地址空間限制(約2-4GB), OS就會拒絕本地內存分配;

  5. JVM拋出 java.lang.OutOfMemoryError: Unable to create new native thread 錯誤。

示例

下面的代碼在一個死循環中創建並啓動很多新線程。代碼執行後, 很快就會達到操作系統的限制, 報出 java.lang.OutOfMemoryError: Unable to create new native thread 錯誤。

 while(true){
    new Thread(new Runnable(){
        public void run() {
            try {
                Thread.sleep(10000000);
            } catch(InterruptedException e) { }        
        }    
    }).start();
} 

原生線程的數量由具體環境決定, 比如, 在 Windows, Linux 和 Mac OS X 系統上:

  • 64-bit Mac OS X 10.9, Java 1.7.0_45 – JVM 在創建 #2031 號線程之後掛掉
  • 64-bit Ubuntu Linux, Java 1.7.0_45 – JVM 在創建 #31893 號線程之後掛掉
  • 64-bit Windows 7, Java 1.7.0_45 – 由於操作系統使用了不一樣的線程模型, 這個錯誤信息似乎不會出現. 創建 #250,000 號線程之後,Java進程依然存在, 但虛擬內存(swap file) 的使用量達到了 10GB, 系統運行極其緩慢,基本上沒法運行了。

所以如果想知道系統的極限在哪兒, 只需要一個小小的測試用例就夠了, 找到觸發 java.lang.OutOfMemoryError: Unable to create new native thread 時創建的線程數量即可。

解決方案

有時可以修改系統限制來避開 Unable to create new native thread 問題. 假如JVM受到用戶空間(user space)文件數量的限制, 像下面這樣,就應該想辦法增大這個值:

[root@dev ~]# ulimit -a
core file size          (blocks, -c) 0
...... 省略部分內容 ......
max user processes              (-u) 1800

更多的情況, 觸發創建 native 線程時的OutOfMemoryError, 表明編程存在BUG. 比如, 程序創建了成千上萬的線程, 很可能就是某些地方出大問題了 —— 沒有幾個程序可以 Hold 住上萬個線程的。

一種解決辦法是執行線程轉儲(thread dump) 來分析具體情況。 一般需要花費好幾個工作日來處理。 當然, 我們推薦使用 Plumbr 來找出問題的根源, 分分鐘幫你搞定。

原文鏈接: https://plumbr.eu/outofmemoryerror/unable-to-create-new-native-thread

翻譯日期: 2017年9月21日

翻譯人員: 鐵錨: http://blog.csdn.net/renfufei

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