单个进程中最大线程数探索

【转载自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,例如

view plaincopy to clipboard

  1. #define MAX_THREADS 50000
  2. DWORD WINAPI ThreadProc( LPVOID lpParam ){
  3. while(1){
  4. Sleep(100000);
  5. }
  6. return 0;
  7. }
  8. int main() {
  9. DWORD dwThreadId[MAX_THREADS];
  10. HANDLE hThread[MAX_THREADS];
  11. for(int i = 0; i < MAX_THREADS; ++i){
  12. hThread[i] = CreateThread(0, 64, ThreadProc, 0, STACK_SIZE_PARAM_IS_A_RESERVATION, &dwThreadId[i]);
  13. if(0 == hThread[i]){
  14. DWORD e = GetLastError();
  15. printf(“%d\r\n”,e);
  16. break;
  17. }
  18. }
  19. ThreadProc(0);
  20. }

Cpp代码

  1. #define MAX_THREADS 50000
  2. DWORD WINAPI ThreadProc( LPVOID lpParam ){
  3. while(1){
  4. Sleep(100000);
  5. }
  6. return 0;
  7. }
  8. int main() {
  9. DWORD dwThreadId[MAX_THREADS];
  10. HANDLE hThread[MAX_THREADS];
  11. for(int i = 0; i < MAX_THREADS; ++i){
  12. hThread[i] = CreateThread(0, 64, ThreadProc, 0, STACK_SIZE_PARAM_IS_A_RESERVATION, &dwThreadId[i]);
  13. if(0 == hThread[i]){
  14. DWORD e = GetLastError();
  15. printf(“%d\r\n”,e);
  16. break;
  17. }
  18. }
  19. ThreadProc(0);
  20. }

服务器端程序设计

如果你的服务器端程序设计成:来一个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)三个方面影响。

最近想测试下Openfire下的最大并发数,需要开大量线程来模拟客户端。对于一个JVM实例到底能开多少

个线程一直心存疑惑,所以打算实际测试下,简单google了把,找到影响线程数量的因素有下面几个:


-Xms


intial Javaheap size


-Xmx


maximum javaheap size


-Xss


the stack size for each thread


系统限制


系统最大可开线程数

测试程序如下:

view plaincopy to clipboard

  1. import java.util.concurrent.atomic.AtomicInteger;
  2. public class TestThread extends Thread {
  3. private static final AtomicInteger count = new AtomicInteger();
  4. public static void main(String[] args) {
  5. while (true)
  6. (new TestThread()).start();
  7. }
  8. @Override
  9. public void run() {
  10. System.out.println(count.incrementAndGet());
  11. while (true)
  12. try {
  13. Thread.sleep(Integer.MAX_VALUE);
  14. } catch (InterruptedException e) {
  15. break;
  16. }
  17. }
  18. }

Java代码

  1. import java.util.concurrent.atomic.AtomicInteger;
  2. public class TestThread extends Thread {
  3. private static final AtomicInteger count = new AtomicInteger();
  4. public static void main(String[] args) {
  5. while (true)
  6. (new TestThread()).start();
  7. }
  8. @Override
  9. public void run() {
  10. System.out.println(count.incrementAndGet());
  11. while (true)
  12. try {
  13. Thread.sleep(Integer.MAX_VALUE);
  14. } catch (InterruptedException e) {
  15. break;
  16. }
  17. }
  18. }

测试环境:

系统: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。

时间: 2024-08-11 01:23:28

单个进程中最大线程数探索的相关文章

用于Nagios中监控服务器进程的最大线程数

最近在刚好一些业务经常把线程跑满,导致服务器资源用完,所以就写了一个脚本用于Nagios下相关进程的最大线程数的监控,Unix的服务器上最大的线程数默认的是1024,当然在业务繁忙的服务器中这样肯定是不够用的,当然在实际生产环境中做初始化调优时一般都会有做过修改,如开启打开文件的最大句柄数等等,一般情况下我们都是修改/etc/security/limits.conf文件,但是要修改最大线程树就要修改/etc/security/limits.d/90-nproc.conf文件了,修改就和修改lim

(转)同一进程中的线程究竟共享哪些资源

线程共享的环境包括:进程代码段.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯).进程打开的文件描述符.信号的处理器.进程的当前目录和进程用户ID与进程组ID. 进程拥有这许多共性的同时,还拥有自己的个性.有了这些个性,线程才能实现并发性.这些个性包括: 1.线程ID      每个线程都有自己的线程ID,这个ID在本进程中是唯一的.进程用此来标识线程. 2.寄存器组的值       由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上时,必须

同一进程中的线程共享的资源有哪些 转载

转自:http://blog.csdn.net/shuilan0066/article/details/7683315 线程共享的环境包括:进程代码段.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯).进程打开的文件描述符.信号的处理器.进程的当前目录和进程用户ID与进程组ID. 进程拥有这许多共性的同时,还拥有自己的个性.有了这些个性,线程才能实现并发性.这些个性包括: 1.线程ID       每个线程都有自己的线程ID,这个ID在本进程中是唯一的.进程用此来标 识线程.

同一进程中的线程究竟共享哪些资源

线程共享的环境包括:进程代码段.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯).进程打开的文件描述符.信号的处理器.进程的当前目录和进程用户ID与进程组ID. 进程拥有这许多共性的同时,还拥有自己的个性.有了这些个性,线程才能实现并发性.这些个性包括: 1.线程ID       每个线程都有自己的线程ID,这个ID在本进程中是唯一的.进程用此来标 识线程. 2.寄存器组的值        由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线 程切换到另一个线程上

[面试题总结及扩展知识]同一进程中的线程共享的资源

又是一道腾讯2014年的面试题: A,栈   B,数据段    C,寄存器组    D,文件描述符 这是解释以及相对应的扩展知识: 线程的共性如下: 线程共享的环境包括:进程代码段. 进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯). 进程打开的文件描述符. 信号的处理器.  进程的当前目录和进程用户ID与进程组ID. 线程的个性如下: 1.线程ID     每个线程都有自己的线程ID,这个ID在本进程中是唯一的.进程用此来标识线程. 2.寄存器组的值     由于线程间是并

分析占用了大量 CPU 处理时间的是Java 进程中哪个线程

下面是详细步骤: 1. 首先确定进程的 ID ,可以使用 jps -v 或者 top 命令直接查看 2. 查看该进程中哪个线程占用大量 CPU,执行 top -H -p [PID] 结果如下: 可以发现编号为 350xx 的共有 9 个线程占用了 100% 的 CPU,好,接下来咱们随便取一个线程 ID ,假设我们想看编号为 35053 这个线程. 首先将 35053 转成 16 进制是 88ED (可以用开源中国在线工具转换) 3. 接下来我们将进程中的所有线程输出到一个文件中,执行:jsta

CentOS7统计某个进程当前的线程数

方式一: cat /proc/[pid]/status 展示结果中,Threads后边对应的数字就是进程拥有的线程数量 方式二: ps hH p 3805|wc -l 原文地址:https://www.cnblogs.com/flying607/p/9436491.html

jps查看java进程中哪个线程在消耗系统资源

jps或ps -ef|grep java可以看到有哪些java进程,这个不用说了.但值得一提的是jps命令是依赖于/tmp下的某些文件 的. 而某些操作系统,定期会清理掉/tmp下的文件,导致jps无法查看到实际存在的java进程.不过jstat, jstack等命令也同样如此,所以当jps列不出进程的时候,这些命令也都不能用了.不在我们此次讨论范围之内. top -p $pid -H  加上-H这个参数后,会列出有哪些线程.这样就可以看到哪个线程id最消耗系统资源了.看到的线程id是10进制的

系统最大fd数和进程中最大fd数

文件描述符,fd? 当每个程序打开文件时,系统会返回一个文件描述符,程序为了处理文件需要引用这个文件描述符,简称fd.fd是一个整数,从0开始,其中0为标准输入,1和2分别为标准输出和标准错误输出.比如printf就默认使用stdout.fd是以进程为单位的,每个进程有一个最大的fd数目,可以使用ulimit设置. 当一个进程尝试使用open函数打开一个新文件时,内核open中会查找当前进程的文件描述符集合,查看待分配的文件描述符是否超出了进程的最大值,如果没有超出最大值,才会为此进程分配一个f