解決Unable to create new native thread


兩種類型的Out of Memory

  1. java.lang.OutOfMemoryError: Java heap space error
    當JVM嘗試在堆中分配對象,堆中空間不足時拋出。一般通過設定JAVA啟動參數-Xmx最小可用內存解決。
  2. java.lang.OutOfMemoryError: Unable to create new native thread
    當JVM向OS申請創建線程,而OS不能分配一個本地線程時拋出。

了解系統參數

系統級最大進程ID

$ sysctl -a | grep kernel.pid_max
kernel.pid_max = 32768
# 輸出結果表示當前系統允許的最大進程數為32768

$ cat /proc/sys/kernel/pid_max
# 命令功用與上述同樣

$ echo 200000 > /proc/sys/kernel/pid_max
# 修改系統級最大進程數為200000,可通過sysctl查看修改

系統級最大線程數

# /proc/sys/kernel/threads-max 限制了系統級最大線程數
$ echo 120000 > /proc/sys/kernel/threads-max
# 修改系統級最大線程數為120000,可通過sysctl查看修改
# Linux沒有每個進程單獨的最大線程數限制

ulimit 檢查OS是否允許用戶申請足夠多的進程(含線程)

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 515005
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 4096
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
# 通過ulimit -u,可見用戶默認的最大進程(含線程)數是1024
# 通過ulimit -s,可見進程默認的最大棧大小是10M

$ ulimit -u 32000
# 更改用戶默認的最大進程(含線程)數為32000

如何驗證 ulimit 中的資源限制?如何查看當前使用量?

Modify 'Soft Limit' of 'Max processes'

Linux對最大線程數的硬控制

number of threads = total virtual memory / (stack size10241024)
從上可知,最大線程數的增加可以通過增加虛擬內存或減少線程棧大小兩種手段。
但是,減少線程棧大小可能會導致棧溢出的異常。

$ ulimit -v newValue
# 更改進程默認的虛擬內存大小

$ ulimit -s newValue
# 更改進程默認的棧大小

max_map_count 間接影響線程數量

max_map_count文件包含限制一個進程可以擁有的VMA(虛擬內存區域)的數量。默認值是65536。
虛擬內存區域是一個連續的虛擬地址空間區域。在進程的生命周期中,每當程序嘗試在內存中映射文件,鏈接到共享內存段,或者分配堆空間的時候,這些區域將被創建。
調優這個值將限制進程可擁有VMA的數量。限制一個進程擁有VMA的總數可能導致應用程序出錯,因為當進程達到了VMA上限但又只能釋放少量的內存給其他的內核進程使用時,操作系統會拋出內存不足的錯誤。
如果你的操作系統在NORMAL區域僅占用少量的內存,那么調低這個值可以幫助釋放內存給內核用。
因值過低拋錯在JVM中可能會呈現Java.lang.OutOfMemoryError: Map failed。詳情參見max_map_count超出導致的OOM

$ echo 'vm.max_map_count=655360' >> /etc/sysctl.conf
# rocketmq系統參數配置了默認值的10倍

$ echo 600000 > /proc/sys/vm/max_map_count
# 更改VMA映射數量為600000,可通過sysctl查看修改

參數之間的關聯

線程本質上是進程,顯著區別在於線程運行在共享內存空間,而進程運行在獨立內存空間
/proc/sys/kernel/pid_max 決定的進程數是運行於獨立內存空間的進程
/proc/sys/kernel/threads-max 決定的線程數是運行於共享內存空間的線程
ulimit -u 指的是一個用戶在同一時間最多能擁有的總進程(含線程)

ulimit 的進程數(含線程)受限於 pid_maxthreads-max

參考資料:
Understanding the difference between pid_max, ulimit -u and thread_max

Maximum number of threads per process in Linux?

查看運行現狀

$ cat /proc/loadavg
0.41 0.45 0.57 3/749 28174
# 輸出結果第四列含有被“/”分割的兩個數字。
# 第一個數字表示當前可執行的內核調度實體(進程、線程)數量,通常小於或等於Cpu的數量。
# 第二個數字表示當前存在的內核調度實體,如上例當前並行存在749個線程

$ ps -elf | wc -l
220
# 當前進程數220(實際上減一行應為219),不包含進程創建的線程

$ ps -elfT | wc -l
385
# 當前線程數385(實際上減一行應為384),包含所有進程及線程

$ ps -p PID -lfT | wc -l
# 指定PID查看線程數,該線程數應該與Jstack生成的Thread Dump上的線程數一致

$ jstack -l PID | grep tid | wc -l
# jstack位於Jdk安裝目錄bin下,用於輸出線程快照

/proc/loadavg的輸出解釋

減少JVM的線程棧大小

  • 一般不這么做,線程棧過小可能拋出java.lang.StackOverflowError異常,常見於舊版Maven編譯失敗。
  • 減少每個線程的棧大小有助於生成更多的線程。
  • 先檢查當前JVM默認的線程棧大小
    $ java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
    intx ThreadStackSize                           = 1024
    # 輸出結果表示JVM默認線程棧大小為1M
    
  • 通過為JVM添加"-Xss",減少線程棧大小。
    JAVA_OPTS="-Xms128m -Xmx1303m -Xss256k"
    # 上述JVM參數表示JVM最小內存大小為128M,最大內存大小為1303M,線程棧大小為256K
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM