【轉載自http://blog.csdn.net/yohoph/article/details/48372805】
windows 操作系統中允許的最大線程數。
===========================================================================
默認情況下,一個線程的棧要預留1M的內存空間
而一個進程中可用的內存空間只有2G,所以理論上一個進程中最多可以開2048個線程
但是內存當然不可能完全拿來作線程的棧,所以實際數目要比這個值要小。
你也可以通過連接時修改默認棧大小,將其改的比較小,這樣就可以多開一些線程。
如將默認棧的大小改成512K,這樣理論上最多就可以開4096個線程。
即使物理內存再大,一個進程中可以起的線程總要受到2GB這個內存空間的限制。
比方說你的機器裝了64GB物理內存,但每個進程的內存空間還是4GB,其中用戶態可用的還是2GB。
如果是同一台機器內的話,能起多少線程也是受內存限制的。每個線程對象都要站用非頁面內存,而
非頁面內存也是有限的,當非頁面內存被耗盡時,也就無法創建線程了。
如果物理內存非常大,同一台機器內可以跑的線程數目的限制值會越來越大。
在Windows下寫個程序,一個進程Fork出2000個左右線程就會異常退出了,為什么?
這個問題的產生是因為windows32位系統,一個進程所能使用的最大虛擬內存為2G,而一個線程的
默認線程棧StackSize為1024K(1M),這樣當線程數量逼近2000時,2000*1024K=2G(大約),
內存資源就相當於耗盡。
MSDN原文:
“The number of threads a process can create is limited by the available virtual memory.
By default, every thread has one megabyte of stack space. Therefore, you can create
at most 2,028 threads. If you reduce the default stack size, you can create more threads. However, your application will have better performance if you create one thread per
processor and build queues of requests for which the application maintains the context
information. A thread would process all requests in a queue before processing requests
in the next queue.”
如何突破2000個限制?
可以通過修改CreateThread參數來縮小線程棧StackSize,例如
- #define MAX_THREADS 50000
- DWORD WINAPI ThreadProc( LPVOID lpParam ){
- while(1){
- Sleep(100000);
- }
- return 0;
- }
- int main() {
- DWORD dwThreadId[MAX_THREADS];
- HANDLE hThread[MAX_THREADS];
- for(int i = 0; i < MAX_THREADS; ++i){
- hThread[i] = CreateThread(0, 64, ThreadProc, 0, STACK_SIZE_PARAM_IS_A_RESERVATION, &dwThreadId[i]);
- if(0 == hThread[i]){
- DWORD e = GetLastError();
- printf(“%d\r\n”,e);
- break;
- }
- }
- ThreadProc(0);
- }
- #define MAX_THREADS 50000
- DWORD WINAPI ThreadProc( LPVOID lpParam ){
- while(1){
- Sleep(100000);
- }
- return 0;
- }
- int main() {
- DWORD dwThreadId[MAX_THREADS];
- HANDLE hThread[MAX_THREADS];
- for(int i = 0; i < MAX_THREADS; ++i){
- hThread[i] = CreateThread(0, 64, ThreadProc, 0, STACK_SIZE_PARAM_IS_A_RESERVATION, &dwThreadId[i]);
- if(0 == hThread[i]){
- DWORD e = GetLastError();
- printf(“%d\r\n”,e);
- break;
- }
- }
- ThreadProc(0);
- }
服務器端程序設計
如果你的服務器端程序設計成:來一個client連接請求則創建一個線程,那么就會存在2000個限制(在
硬件內存和CPU個數一定的情況下)。建議如下:
The “one thread per client” model is well-known not to scale beyond a dozen clients
or so. If you‘re going to be handling more than that many clients simultaneously,
you should move to a model where instead of dedicating a thread to a client, you
instead allocate an object. (Someday I’ll muse on the duality between threads and
objects.) Windows provides I/O completion ports and a thread pool to help you
convert from a thread-based model to a work-item-based model.
1. Serve many clients with each thread, and use nonblocking I/O and level-triggered
readiness notification
2. Serve many clients with each thread, and use nonblocking I/O and readiness
change notification
3. Serve many clients with each server thread, and use asynchronous I/O
--------------------
附:Win32將低區的2GB留給進程使用, 高區的2GB則留給系統使用。
Linux將高位1GB留給內核,低位3GB留給進程
linux系統中允許的最大線程數
==========================================================================================
JVM中可生成的最大Thread數量
JVM中可以生成的最大數量由JVM的堆內存大小、Thread的Stack內存大小、系統最大
可創建的線程數量(Java線程的實現是基於底層系統的線程機制來實現的,Windows下_beginthreadex,Linux下pthread_create)三個方面影響。
-Xms |
intial Java heap size |
-Xmx |
maximum java heap size |
-Xss |
the stack size for each thread |
系統限制 |
系統最大可開線程數 |
- import java.util.concurrent.atomic.AtomicInteger;
- public class TestThread extends Thread {
- private static final AtomicInteger count = new AtomicInteger();
- public static void main(String[] args) {
- while (true)
- (new TestThread()).start();
- }
- @Override
- public void run() {
- System.out.println(count.incrementAndGet());
- while (true)
- try {
- Thread.sleep(Integer.MAX_VALUE);
- } catch (InterruptedException e) {
- break;
- }
- }
- }
- import java.util.concurrent.atomic.AtomicInteger;
- public class TestThread extends Thread {
- private static final AtomicInteger count = new AtomicInteger();
- public static void main(String[] args) {
- while (true)
- (new TestThread()).start();
- }
- @Override
- public void run() {
- System.out.println(count.incrementAndGet());
- while (true)
- try {
- Thread.sleep(Integer.MAX_VALUE);
- } catch (InterruptedException e) {
- break;
- }
- }
- }
測試環境:
系統:Ubuntu 10.04 Linux Kernel 2.6 (32位)
內存:2G
JDK:1.7
測試結果:
◆ 不考慮系統限制
-Xms |
-Xmx |
-Xss |
結果 |
1024m |
1024m |
1024k |
1737 |
1024m |
1024m |
64k |
26077 |
512m |
512m |
64k |
31842 |
256m |
256m |
64k |
31842 |
在創建的線程數量達到31842個時,系統中無法創建任何線程。
由上面的測試結果可以看出增大堆內存(-Xms,-Xmx)會減少可創建的線程數量,增大線程棧內存
(-Xss,32位系統中此參數值最小為60K)也會減少可創建的線程數量。
◆ 結合系統限制
線程數量31842的限制是是由系統可以生成的最大線程數量決定的:/proc/sys/kernel/threads-max,
可其默認值是32080。修改其值為10000:echo 10000 > /proc/sys/kernel/threads-max,
修改后的測試結果如下:
-Xms |
-Xmx |
-Xss |
結果 |
256m |
256m |
64k |
9761 |
這樣的話,是不是意味着可以配置盡量多的線程?再做修改:echo 1000000 > /proc/sys/kernel/threads-max,
修改后的測試結果如下:
-Xms |
-Xmx |
-Xss |
結果 |
256m |
256m |
64k |
32279 |
128m |
128m |
64k |
32279 |
發現線程數量在達到32279以后,不再增長。查了一下,32位Linux系統可創建的最大pid數是32678,
這個數值可以通過/proc/sys/kernel/pid_max來做修改(修改方法同threads-max),但是在32系
統下這個值只能改小,無法更大。在threads-max一定的情況下,修改pid_max對應的測試結果如下:
pid_max |
-Xms |
-Xmx |
-Xss |
結果 |
1000 |
128m |
128m |
64k |
582 |
10000 |
128m |
128m |
64k |
9507 |
在Windows上的情況應該類似,不過相比Linux,Windows上可創建的線程數量可能更少。基於線
總結:
JVM中可以生成的最大數量由JVM的堆內存大小、Thread的Stack內存大小、系統最大可創建的線程數量
(Java線程的實現是基於底層系統的線程機制來實現的,Windows下_beginthreadex,Linux下
pthread_create)三個方面影響。具體數量可以根據Java進程可以訪問的最大內存(32位系統上一般2G)、
堆內存、Thread的Stack內存來估算。
序:
在64位Linux系統(CentOS 6, 3G內存)下測試,發現還有一個參數是會限制線程數量:
max user process(可通過ulimit –a查看,默認值1024,通過ulimit –u可以修改此值),
這個值在上面的32位Ubuntu測試環境下並無限制。
將threads-max,pid_max,max user process,這三個參數值都修改成100000,-Xms,
-Xmx盡量小(128m,64m),-Xss盡量小(64位下最小104k,可取值128k)。事先預測在
這樣的測試環境下,線程數量就只會受限於測試環境的內存大小(3G),可是實際的測試結果是
線程數量在達到32K(32768,創建的數量最多的時候大概是33000左右)左右時JVM是拋出警告:
Attempt to allocate stack guard pages failed,然后出現OutOfMemoryError無法創建本
地線程。查看內存后發現還有很多空閑,所以應該不是內存容量的原因。Google此警告無果,
暫時不知什么原因,有待進一步研究。
序2:今天無意中發現文章[7],馬上試了下,果然這個因素會影響線程創建數量,按文中描述把/proc/sys/vm/max_map_count的數量翻倍,從65536變為131072,創建的線程總數
量達到65000+,電腦基本要卡死(3G內存)… 簡單查了下這個參數的作用,在[8]中的描述如下:
“This file contains the maximum number of memory map areas a process may have.
Memory map areas are used as a side-effect of calling malloc, directly by mmap and
mprotect, and also when loading shared libraries.
While most applications need less than a thousand maps, certain programs,
particularly malloc debuggers, may consume lots of them, e.g., up to one or two
maps per allocation.
The default value is 65536.”
OK,這個問題總算完滿解決,最后總結下影響Java線程數量的因素:
Java虛擬機本身:-Xms,-Xmx,-Xss;
系統限制:
/proc/sys/kernel/pid_max,
/proc/sys/kernel/thread-max,
max_user_process(ulimit -u),
/proc/sys/vm/max_map_count。