您的位置:首页 > 其它

JVM能够开启多少线程

2015-11-11 21:21 357 查看
最近在看<<Java并发编程实战>>一书过程中,在Task Execution一节中讲到,针对每个任务都启动一个线程来处理,尤其在大量创建线程的场景下,会给工程实践带来很多问题.

1)线程的创建和销毁都是有开销的。线程的创建需要时间,如果针对每个任务都启动线程处理,则无疑会造成请求不能得到尽快处理。如果请求比较频繁而且轻量级,为每个请求创建新线程会消耗大量资源。

2)资源消耗.线程会消耗系统资源,尤其是内存。如果可运行的线程(Runnable状态)数量大于可用处理器数量,那么有些线程会得不到调度。这些线程一旦数量太多就会占用大量内存,给GC带来回收压力。同时大量的线程对CPU的竞争带来的上下文切换也会影响系统性能。如果已经有足够的线程来让CPU保持忙碌,那么再创建新的线程非但不能提高CPU利用率,反而会降低性能。

3)稳定性。能够创建的线程数量是有上限的。这个数量与平台相关,也受限于多个因素:JVM启动参数(-Xss),创建Thread时在构造函数中指定的线程栈大小(stack size)和底层操作系统对线程数的限制。

从第三点可见,线程数量和线程栈大小(通过-Xss或stack size指定,一般为0.5M-1M)、可用内存和操作系统限制有关系。因此本文讨论一下,究竟JVM最多能够开启多少线程。

不考虑OS限制的情况下,RAM越大,线程栈越小,可以创建的线程就越多。一个理论上的计算公式如下:

Max number of threads(最大线程数)=(进程最大可用内存-JVM分配内存-OS Reserved Memory(JNI,本地方法栈))/thread stack size(线程栈大小)

对于32位操作系统,允许创建的线程数量一个重要的制约因素便是内存RAM。由于寻址空间仅仅有2^32byte(4GB),按照线程栈大小为0.5M来计算,最大线程数为2^13,即不到1万。如果按照线程栈大小为1M来计算,最大线程数不过数千。

对于64为操作系统,内存不再是最大制约因素,最大线程数受限于Linux内核.以我的Ubuntu 64-bit 操作系统,16GB RAM为例,执行如下代码:

package snow.rabbit.java.thread;
/**
*
* @author lawrence
*
*/
public class TestMaxSupportThreads {

private static final class TestThread extends Thread
{
private final int number;

public TestThread(int number) {
this.number = number;
}

public TestThread(int number,int stackSize) {
super(null, null, "Thread-Hi-" +number,stackSize);
this.number = number;
}

@Override
public void run() {
if (number%100 ==0)
{
System.out.println("Thread "+number+" started.");
}
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
int i=0;
try {
for (i=0; i < 200; i++)
{
Thread t=new TestThread(i,2<<19);//512K
t.start();
}
} catch (Throwable e) {
System.out.println("Error occured when creating thread "+i);
e.printStackTrace();
}
}
}


输出结果如下:

Thread 32100 started.
Error occured when creating thread 32129
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:640)
at TestMaxSupportThreads.main(TestMaxSupportThreads.java:37)


此时内存还有很大剩余,但是线程创建到32129时便出现异常,这是由于OS的限制导致,和kernal/ulimit有关。具体见如下设置:

1)/proc/sys/kernel/pid_max :系统允许的最大pid

与用户态不同,对于Linux内核而言,进程和线程之间的区别并不大,线程也不过是共享内存空间的进程。每个线程都是一个轻量级进程(Light Weight Process),都有自己的唯一PID(或许叫TID更合适一些)和一个TGID(Thread group ID),TGID是启动整个进程的thread的PID.

简单来说,当一个进程被创建的时候,它其实是一个PID和TGID数值相同线程。当线程A启动线程B时,线程B会有自己的唯一PID,但它的TGID会从A继承而来。这样通过PID线程可以独立得到调度,而相同的TGID可以知道哪些线程属于同一个进程,这样可以共享资源(RAM,虚拟内存、文件等)。

通过ps -fL可以看到LWP轻量级进程信息:
lizhong@lizhongpc:~$ ps -fL
UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
lizhong   4167  2252  4167  0    1 14:49 pts/2    00:00:00 -bash
lizhong   6592  4167  6592  0    1 16:40 pts/2    00:00:00 ps -fL

我们启动200个线程后,可以看到LWP显著增加。
lizhong@lizhongpc:~$ ps -fL
UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
lizhong   4167  2252  4167  0    1 14:49 pts/2    00:00:00 -bash
lizhong   6828  4167  6828  0    1 16:40 pts/2    00:00:00 ps -fL


2)/proc/sys/kernel/threads-max :系统支持的最大线程数

3)max_user_process(ulimit -u):每个用户允许的最大进程数

4)/proc/sys/vm/max_map_count: Linux支持虚拟内存,也就是交换空间,可以把磁盘的一部分作为RAM的扩展,逻辑存储和物理存储的映射就要保存在地址映射表中。max_map_count限制了线程可以拥有的VMAs (Virtual Memory Areas)。

将pid_max,threads-max,max_user_process和max_map_count都改为4194000后,分别将Thread构造函数中stackSize指定为1M,512K,64K,达到的最大线程数分别是423100,626400,626400.

从中我们可以看出两点:

1)进程可用内存不仅仅局限于RAM,还包括Swap分区(虚拟内存),在线程不断创建的过程中,首先空闲的RAM不断减少,然后是空闲Swap不断减少。

2)我们在线程构造函数中指定了stackSize分别为512K和64K,但达到的最大线程数是相差无几的,说明JVM并非绝对按照我们指定的大小为线程分配内存。

综上所述,JVM最大开启线程数取决于可用内存(包括虚拟内存)和stack size, 在OS层面又和pid_max、threads-max、max_user_process和max_map_count相关。

下面是我在16G内存下,stack size 设置为512K,各项OS内核参数调整到最大,创建线程的过程中不断跟踪内存情况和轻量级线程情况的截图:

yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935        579      15355         41         50        245
-/+ buffers/cache:        282      15652
Swap:        19070          0      19070
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935       4805      11130         41         50        245
-/+ buffers/cache:       4508      11426
Swap:        19070          0      19070
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935       7013       8921         41         50        245
-/+ buffers/cache:       6717       9217
Swap:        19070          0      19070
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935       8394       7540         41         50        245
-/+ buffers/cache:       8098       7836
Swap:        19070          0      19070
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935      11192       4742         41         50        245
-/+ buffers/cache:      10895       5039
Swap:        19070          0      19070
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935      12630       3304         41         50        245
-/+ buffers/cache:      12334       3600
Swap:        19070          0      19070
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935      14248       1686         41         50        245
-/+ buffers/cache:      13951       1983
Swap:        19070          0      19070
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935      15091        843         41         50        245
-/+ buffers/cache:      14794       1140
Swap:        19070          0      19070
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935      15758        176         27          0         32
-/+ buffers/cache:      15725        209
Swap:        19070        635      18435
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935      15787        147          2          0         13
-/+ buffers/cache:      15773        161
Swap:        19070       8706      10364
yangy@yangy:~/lizhongspace$ free -m
total       used       free     shared    buffers     cached
Mem:         15935      15788        146          7          0         26
-/+ buffers/cache:      15761        173
Swap:        19070      10723       8347
yangy@yangy:~/lizhongspace$ ps -fL
UID          PID    PPID     LWP  C NLWP STIME TTY          TIME CMD
yangy       2237    2236    2237  0    1 21:20 pts/11   00:00:00 -bash
yangy     631902    2237  631902  0    1 21:31 pts/11   00:00:00 ps -fL


以上内容同时发表在我的个人博客清欢de个人博客中,欢迎大家访问。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: