zoukankan      html  css  js  c++  java
  • 最大线程数量

    最近面试的时候遇到一个关于java中最多能创建多少线程的问题,结果瞎答一通,尴尬了,回来看了一篇博客,人家写的很好,转载一下,原文链:https://www.cnblogs.com/princessd8251/articles/3914434.html

    JVM最大创建线程数量由JVM堆内存大小、线程的Stack内存大小、系统最大可创建线程数(Java线程的实现是基于底层系统的线程机制来实现的,Windows下_beginthreadex,Linux下pthread_create)三个方面影响。具体如下:

    -Xms  最小堆内存
    -Xmx  最大堆内存
    -Xss   设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M
    操作系统限制 系统最大可开线程数,主要受以下几个参数影响

            /proc/sys/kernel/pid_max

            /proc/sys/kernel/thread-max

            max_user_process(ulimit -u) 在64位Linux系统(CentOS 6, 3G内存)下测试,发现还有一个参数是会限制线程数量:max user process(可通过ulimit –a查看,默认值1024,通过ulimit –u可以修改此值),这个值在上面的32位Ubuntu测试环境下并无限制

            /proc/sys/vm/max_map_count

    将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无法创建本地线程。查看内存后发现还有很多空闲,所以应该不是内存容量的原因。/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.”

    操作系统最大线程数限制

    32位Linux系统可创建的最大pid数是32678,这个数值可以通过/proc/sys/kernel/pid_max来做修改(修改其值为10000:echo 10000 > /proc/sys/kernel/pid_max),但是在32系统下这个值只能改小,无法改大。

    Windows可创建的线程数量比linux可能更少。

    在64位Linux系统(CentOS 6)下,还有一个参数会限制线程数量:max user process(可通过ulimit –a查看,默认值1024,通过ulimit –u可以修改此值)

    测试程序如下:

    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.     } 

    测试环境:

    系统: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内存来估算。

    一、模拟问题:

    首先我们通过下面这个测试程序 来认识这个问题:
    运行的环境 (有必要说明一下,不同环境会有不同的结果):32位 Windows XP,Sun JDK 1.6.0_18, eclipse 3.4,
    测试程序:

    Java代码  收藏代码
    1. import java.util.concurrent.CountDownLatch;  
    2.   
    3. public class TestNativeOutOfMemoryError {  
    4.   
    5.     public static void main(String[] args) {  
    6.   
    7.         for (int i = 0;; i++) {  
    8.             System.out.println("i = " + i);  
    9.             new Thread(new HoldThread()).start();  
    10.         }  
    11.     }  
    12.   
    13. }  
    14.   
    15. class HoldThread extends Thread {  
    16.     CountDownLatch cdl = new CountDownLatch(1);  
    17.   
    18.     public HoldThread() {  
    19.         this.setDaemon(true);  
    20.     }  
    21.   
    22.     public void run() {  
    23.         try {  
    24.             cdl.await();  
    25.         } catch (InterruptedException e) {  
    26.         }  
    27.     }  
    28. }  

    不指定任何JVM参数,eclipse中直接运行输出,看到了这位朋友了吧:
    i = 5602 
    Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Thread.java:597)
        at TestNativeOutOfMemoryError.main(TestNativeOutOfMemoryError.java:20)

    二、分析问题:

    这个异常问题本质原因是我们创建了太多的线程,而能创建的线程数是有限制的,导致了异常的发生。能创建的线程数的具体计算公式如下: 
    (MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
    MaxProcessMemory 指的是一个进程的最大内存
    JVMMemory         JVM内存
    ReservedOsMemory  保留的操作系统内存
    ThreadStackSize      线程栈的大小

    在java语言里, 当你创建一个线程的时候,虚拟机会在JVM内存创建一个Thread对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。 


    结合上面例子我们来对公式说明一下: 
    MaxProcessMemory 在32位的 windows下是 2G
    JVMMemory   eclipse默认启动的程序内存是64M
    ReservedOsMemory  一般是130M左右
    ThreadStackSize 32位 JDK 1.6默认的stacksize 325K左右
    公式如下:
    (2*1024*1024-64*1024-130*1024)/325 = 5841 
    公式计算所得5841,和实践5602基本一致(有偏差是因为ReservedOsMemory不能很精确) 

    由公式得出结论:你给JVM内存越多,那么你能创建的线程越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread。 

    咦,有点背我们的常理,恩,让我们来验证一下,依旧使用上面的测试程序,加上下面的JVM参数,测试结果如下: 
    ThreadStackSize      JVMMemory                    能创建的线程数
    默认的325K             -Xms1024m -Xmx1024m    i = 2655
    默认的325K               -Xms1224m -Xmx1224m    i = 2072
    默认的325K             -Xms1324m -Xmx1324m    i = 1753
    默认的325K             -Xms1424m -Xmx1424m    i = 1435
    -Xss1024k             -Xms1424m -Xmx1424m    i = 452 
    完全和公式一致。 

    三、解决问题: 
    1, 如果程序中有bug,导致创建大量不需要的线程或者线程没有及时回收,那么必须解决这个bug,修改参数是不能解决问题的。
    2, 如果程序确实需要大量的线程,现有的设置不能达到要求,那么可以通过修改MaxProcessMemory,JVMMemory,ThreadStackSize这三个因素,来增加能创建的线程数:
    a, MaxProcessMemory 使用64位操作系统
    b, JVMMemory   减少JVMMemory的分配
    c, ThreadStackSize  减小单个线程的栈大小

    参考资料:

    1. http://blog.krecan.net/2010/04/07/how-many-threads-a-jvm-can-handle/

    2. http://www.cyberciti.biz/tips/maximum-number-of-processes-linux-26-kernel-can-handle.html

    3. http://geekomatic.ch/2010/11/24/1290630420000.html

    4. http://stackoverflow.com/questions/763579/how-many-threads-can-a-java-vm-support

    5. http://www.iteye.com/topic/1035818

    6. http://hi.baidu.com/hexiong/blog/item/16dc9e518fb10c2542a75b3c.html

    7. https://listman.redhat.com/archives/phil-list/2003-August/msg00025.html

    8. http://www.linuxinsight.com/proc_sys_vm_max_map_count.html

    http://jzhihui.iteye.com/blog/1271122

    http://sesame.iteye.com/blog/622670

  • 相关阅读:
    matlab矩阵中如何去掉重复的行;如何找到相同的行,并找到其位置
    Coursera 机器学习 第9章(下) Recommender Systems 学习笔记
    机器学习基石笔记1——在何时可以使用机器学习(1)
    Coursera 机器学习 第9章(上) Anomaly Detection 学习笔记
    matlab安装过程的被要求的配置程序
    jdk环境变量配置
    Coursera 机器学习 第8章(下) Dimensionality Reduction 学习笔记
    Coursera 机器学习 第8章(上) Unsupervised Learning 学习笔记
    Coursera 机器学习 第7章 Support Vector Machines 学习笔记
    linux服务器---squid限制
  • 原文地址:https://www.cnblogs.com/by-my-blog/p/11547201.html
Copyright © 2011-2022 走看看