解決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 中的資源限制?如何查看當前使用量?java

Modify 'Soft Limit' of 'Max processes'linux

Linux對最大線程數的硬控制

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

$ 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超出致使的OOMjvm

$ 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 指的是一個用戶在同一時間最多能擁有的總進程(含線程)spa

ulimit 的進程數(含線程)受限於 pid_maxthreads-max操作系統

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

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的輸出解釋unix

減小JVM的線程棧大小

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