記一次java.lang.OutOfMemoryError: unable to create new native thread

       記錄一次因爲第三方工具使用不當引發的服務器內存被耗盡,導致Java服務無法創建新線程的OOM,當時太忙沒有抽出時間來記錄,現在只能憑藉記憶和其他文章來還原當時問題的排查流程日後備用。

問題發現:

       這個問題是在開發新需求時,在測試環境被暴露出來的,測試反饋說所有的接口突然都調不通了。

解決流程:

1、接口報異常連上服務器tail日誌,這個是當時日誌報出的異常現場截圖:(異常描述的清晰明瞭:無法創建新的本機線程)

2、通過對新提交的代碼Review並沒有發現可能出現問題的地方(後來發現確實是代碼的問題)。

3、代碼沒發現問題的話,試着修改了Linux系統關於JVM的配置如:-Xss、-Xms、-Xmx等,然並卵。

        -Xss 128k:設置每個線程的堆棧大小。JDK5.0以後每個線程堆 棧大小爲1M,以前每個線程堆棧大小爲256K。在相同物理內存下,Xss越大,每個線程的大小就越大,佔用的內存越多,能容納的線程就越少;Xss越小,則遞歸的深度越小,容易出現棧溢出 java.lang.StackOverflowError。。但是操作系統對一 個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。線程棧的大小是個雙刃劍,如果設置過小,可能會出現棧溢出,特別是在該線程內有遞歸、大的循環時出現溢出的可能性更大,如果該值設置過大,就有影響到創建棧的數量,如果是多線程的應用,就會出現內存溢出的錯誤。

4、幾番周測,在整理思路後,決定首要任務就是如何重現該問題,於是編寫測試程序,測試出操作系統最大能夠創建的線程數:

import java.util.concurrent.CountDownLatch;

public class TestNativeOutOfMemoryError {

    public static void main(String[] args) {

        for (int i = 0;; i++) {
            System.out.println("i = " + i);
            new Thread(new HoldThread()).start();
        }
    }

}

class HoldThread extends Thread {
    CountDownLatch cdl = new CountDownLatch(1);

    public HoldThread() {
        this.setDaemon(true);
    }

    public void run() {
        try {
            cdl.await();
        } catch (InterruptedException e) {
        }
    }
}

運行後:

  i = 1002
  Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
      at java.lang.Thread.start0(Native Method)
      at java.lang.Thread.start(Thread.java:597)
      at TestNativeOutOfMemoryError.main(TestNativeOutOfMemoryError.java:20)

  問題重現,在反覆運行幾次後發現,生產系統最大隻能創建1002多個線程。而我本地PC電腦都可以創建2500左右。

操作系統64位CentOS release 6.7 (Final),java version "1.8.0_101",64G內存。

  感覺原因快找到了,切換到運行賬戶使用命令:

  生產上所有程序都是在jenkins賬戶下運行,於是查看該賬戶下所有的線程數總和爲908,也即是說,隨時都可能會超過1002,導致內存溢出。查看看當前運行的線程數命令爲:

[jenkins@localhost ~]$ ps -eLf | wc -l

  原因找到,操作系統對運行程序的賬戶有最大線程數限制。

[root@localhost ~]# cat /etc/security/limits.d/90-nproc.conf

 

打開後發現除了root,其他賬戶都限制在8096個。

  於是增加一條:jenkins     soft    nproc     20000

  爲什麼設置爲20000,因爲測試後發現,在運行到35000左右,系統就報內存溢出了,操作系統所有命令都不能使用,因此將程序最大線程數限制在20000。

修改後問題未解決(因爲問題不是出現在OS上),但是操作系統可創建的線程數增加了很多。

總結:

一:修改JVM的-Xss參數,減小新創建線程的內存佔用,相同的物理內存下可以創建更多的線程。

二:修改/etc/security/limits.d/90-nproc.conf中關於操作系統對用戶最大線程數的限制

 

備註:

Apache的HttpClient據說很強大,這次的原因是HttpClient的資源釋放和超時處理的配置不當,導致每次請求都會新建線程,並且新建的線程不會被回收,最終將服務器的內存耗盡。關於HttpClient的這個問題可以參考這篇文章

 

文章參考:

https://www.cnblogs.com/myshare/archive/2016/02/02/5177135.html

https://blog.csdn.net/penghaiping1001/article/details/73199300

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