線程池使用不當導致服務器內存耗盡

  最近一個項目上線後,服務器磁盤兩三天報一下磁盤100%的異常,開始以爲是磁盤問題,因爲這個服務器上有一個rsync的定時任務,10分鐘一次,用於同步靜態文件,大概1萬個html文件,正常不到1分鐘同步完成。

  但是運維幫忙重啓後,兩三天磁盤又報警,看了一下內存使用,內存從重啓後,一直緩慢的增加,沒有回落,直到報警前的90%以上。看了一下監控系統統計的線程數,發現線程數從上次重啓後,一次在增加,沒有穩定在一個值範圍。正常使用線程池,這個應該是在一個固定值左右小範圍波動的。但是報警的服務器,線程數一直在增加。

  登上服務器,用jstack -l pid > jstack.log 看了一下內存線程的情況,統記了一下線程的狀態信息,發現WAITING(parking)的線程數有13000多個(使用統計狀態的命令:grep --color=auto -A2 -B2 '.Thread.State:' jstack.log | awk -F ".Thread.State:" '{print $2}' | awk -F "Unsafe.park" '{print $1}' | sort | uniq -c | sort -k1,1nr )。自此,問題應該初步鎖定是線程出了問題。

  查看代碼,發現一個類中使用了ExecutorService executorService = Executors.newFixedThreadPool(20);,但是沒有使用static,導致這個類每次創建時,都會創建一次線程池,而之前的創建的線程又沒有銷燬,從而沒有銷燬的線程一直佔有着內存,而且服務器上日誌開始有大量的java.lang.OutOfMemoryError: unable to create new native thread錯誤。

  那爲什麼java線程沒有銷燬,會導致服務器內存達到100%呢

因爲在java語言裏, 當你創建一個線程的時候,虛擬機會在JVM內存創建一個Thread對象同時創建一個操作系統線程,而這個系統線程的內存用的不是JVMMemory,而是系統中剩下的內存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。 其中MaxProcessMemory指的是一個進程的最大內存,JVMMemory是JVM內存,ReservedOsMemory是保留的操作系統內存,ThreadStackSize是線程棧的大小。


  所以在使用static ExecutorService executorService = new ThreadPoolExecutor(20, 40, 60L,

            TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4000));修改了相關的線程使用後,程序目前穩定了。


參考:

  1.解決 - java.lang.OutOfMemoryError: unable to create new native thread

  2.Java中容易踩到的“坑”系列之線程池篇

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