本文來自: PerfMa技術社區PerfMa(笨馬網絡)官網java
雖然這篇文章的標題打着JVM源碼分析的旗號,不過本文不只僅從 JVM 源碼角度來分析,更多的來自於 Linux Kernel 的源碼分析,今天要說的是 JVM 裏比較常見的一個問題。linux
這個問題可能有幾種表述網絡
java.lang.OutOfMemoryError: unable to create new native thread
的異常到底是怎麼回事不過我這裏先聲明下可能不能徹底百分百將各類因素都理出來,由於畢竟我不是作 Linux Kernel 開發的,還有很多細節沒有注意到的,我將我能分析到的因素和你們分享一下,若是你們在平時工做中還碰到別的因素,歡迎在文章下面留言,讓更多人蔘與進來討論數據結構
線程你們都熟悉,new Thread().start()
即會建立一個線程,這裏我首先指出一點new Thread()
其實並不會建立一個真正的線程,只有在調用了 start 方法以後纔會建立一個線程,這個你們分析下 Java 代碼就知道了,Thread 的構造函數是純 Java 代碼,start 方法會調到一個 native 方法 start0 裏,而 start0 其實就是JVM_StartThread
這個方法。jvm
從上面代碼裏首先要你們關注下最後的那個 if 判斷 if (native_thread->osthread() == NULL)
,若是 osthread 爲空,那將會拋出你們比較熟悉的 unable to create new native thread OOM
異常,所以 osthread 爲空很是關鍵,後面會看到什麼狀況下osthread會爲空。函數
另外你們應該注意到了native_thread = new JavaThread(&thread_entry, sz)
,在這裏纔會真正建立一個線程。源碼分析
上面代碼裏的os::create_thread(this, thr_type, stack_sz)
會經過pthread_create
來建立線程,而 Linux 下對應的實現以下:學習
若是在 new OSThread
的過程當中就失敗了,那顯然 osthread 爲 NULL,那再回到上面第一段代碼,此時會拋出java.lang.OutOfMemoryError: unable to create new native thread
的異常,而什麼狀況下new OSThread
會失敗,好比說內存不夠了,而這裏的內存實際上是 C Heap,而非 Java Heap,因而可知從 JVM 的角度來講,影響線程建立的因素包括了 Xmx,MaxPermSize,MaxDirectMemorySize,ReservedCodeCacheSize 等,由於這些參數會影響剩餘的內存this
另外注意到若是pthread_create
執行失敗,那經過thread->set_osthread(NULL)
會設置空值,這個時候 osthread 也爲 NULL,所以也會拋出上面的 OOM 異常,致使建立線程失敗,所以接下來要分析下 pthread_create 失敗的因素。spa
stack_size
pthread_create 的實如今 glibc 裏,
上面我主要想說的一段代碼是int err = ALLOCATE_STACK (iattr, &pd)
,顧名思義就是分配線程棧,簡單來講就是根據 iattr 裏指定的 stackSize,經過 mmap 分配一塊內存出來給線程做爲棧使。
那咱們來講說 stackSize,這個你們應該都明白,線程要執行,要有一些棧空間,試想一下,若是分配棧的時候內存不夠了,是否是建立確定失敗?而 stackSize 在 JVM 下是能夠經過 -Xss 指定的,固然若是沒有指定也有默認的值,下面是 JDK6 以後(含)默認值的狀況。
若是棧分配成功,那接下來就要建立線程了,大概邏輯以下
而create_thread實際上是調用的系統調用clone
系統調用這塊就切入到了 Linux Kernel 裏
clone 系統調用最終會調用do_fork
方法,接下來經過剖解這個方法來分析 Kernel 裏還存在哪些因素。
max_user_processes
先看這麼一段,這裏其實就是判斷用戶的進程數有多少,你們知道在linux下,進程和線程其數據結構都是同樣的,所以這裏說的進程數能夠理解爲輕量級線程數,而這個最大值是能夠經過ulimit -u
能夠查到的,因此若是當前用戶起的線程數超過了這個限制,那確定是不會建立線程成功的,能夠經過ulimit -u value
來修改這個值
max_map_count
在這個過程當中不乏有 mallo c的操做,底層是經過系統調用 brk 來實現的,或者上面提到的棧是經過 mmap 來分配的,不論是 malloc 仍是 mmap,在底層都會有相似的判斷。
若是進程被分配的內存段超過sysctl_max_map_count
就會失敗,而這個值在 linux 下對應/proc/sys/vm/max_map_count
,默認值是 65530,能夠經過修改上面的文件來改變這個閾值
max_threads
還存在max_threads
的限制,代碼以下:
若是要修改或者查看能夠經過/proc/sys/kernel/threads-max
來操做, 這個值是受到物理內存的限制,在fork_init
的時候就計算好了。
pid_max
pid 也存在限制
而alloc_pid
的定義以下
在alloc_pidmap
中會判斷pid_max
,而這個值的定義以下
這個值能夠經過 /proc/sys/kernel/pid_max
來查看或者修改
經過對 JVM,glibc,Linux kernel 的源碼分析,咱們暫時得出了一些影響線程建立的因素,包括
因爲對 kernel 的源碼研讀時間有限,不必定總結完整,你們能夠補充。
一塊兒來學習吧: