什么是JVM可以创建的阈值限制线程数

zhu*_*zhu 5 java multithreading jvm

编辑:

正如@Petesh所说,我达到了kern.num_taskthreads极限而不是整体线程限制,这限制了单个进程的线程数.

sysctl kern.num_taskthreads方法是:

kern.num_taskthreads: 2048
Run Code Online (Sandbox Code Playgroud)

当我使用VM args时-XX:ThreadStackSize=1g,我只能创建122个线程; 用-XX:ThreadStackSize=2g,创建了58个线程.这很合理.

但是仍然很奇怪,无论我如何改变-Xssargs,结果始终是2031.该-XssARGS似乎只适用于主线程这我不知道现在.

原始问题:

我运行了一个测试,以找出一个JVM可以创建多少个线程.当我调整了JVM ARGS,-Xmx并且-Xss,其结果并没有改变.

这是代码:

public class ThreadTest {
    public static void main(String[] args) {
        int count = 0;
        try {
            while (true) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            TimeUnit.SECONDS.sleep(360);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
                thread.start();
                System.out.println(count);
            }

        } catch (Error e) {
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

和操作系统信息:

  • 型号名称:MacBook Pro
  • 型号标识符:MacBookPro11,4
  • 处理器名称:Intel Core i7
  • 处理器速度:2.2 GHz
  • 处理器数量:1
  • 核心总数:4
  • L2缓存(每个核心):256 KB
  • L3缓存:6 MB
  • 内存:16 GB

java版本:

? java -version                                                                                                                                                          
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Dynamic Code Evolution 64-Bit Server VM (build 25.71-b01-dcevmlight-1, mixed mode)
Run Code Online (Sandbox Code Playgroud)

结果: 在此输入图像描述

ulimit -a: 在此输入图像描述

sysctl kern.num_threads:

kern.num_threads: 10240
Run Code Online (Sandbox Code Playgroud)

Pet*_*esh 4

所有这些东西都是操作系统特定的 - 在 OSX 的情况下,您有一个每个进程的线程限制,不能超过sysctl kern.num_taskthreads. 您创建的线程数量限制以及虚拟机创建的线程的开销似乎表明您已达到该限制。

-XX:ThreadStackSize和之间的区别-Xss<size>有点奇怪。在本例中,我的分析基于 OSX oracle java vm(您表明您正在使用不同的 VM 运行)。

-Xss将堆栈大小设置为该字节数。存储它的变量将其除以 1024。然而,由于它的计算方式,该值最终成为无意义的值(64 位 jvm,在 Linux 和 osx 上检查) - 这是一些非常糟糕的溢出数学:

for i in {1..8}; do echo "${i}G:"; java -Xss${i}g -XX:+PrintFlagsFinal -version 2>&1 | grep ' ThreadStack'; done
1G:
     intx ThreadStackSize                          := 1048576                             {pd product}
2G:
     intx ThreadStackSize                          := 18014398507384832                    {pd product}
3G:
     intx ThreadStackSize                          := 18014398508433408                    {pd product}
4G:
     intx ThreadStackSize                          := 0                                   {pd product}
5G:
     intx ThreadStackSize                          := 1048576                             {pd product}
6G:
     intx ThreadStackSize                          := 18014398507384832                    {pd product}
7G:
     intx ThreadStackSize                          := 18014398508433408                    {pd product}
8G:
     intx ThreadStackSize                          := 0                                   {pd product}
Run Code Online (Sandbox Code Playgroud)

当我们将其与此进行比较时,-XX:ThreadStackSize我们会得到不同的图片:

首先,这些值按 1024 倍缩放 - 即,所有请求的值实际上都是堆栈大小的 KB 数。

这意味着-XX:ThreadstackSize需要将 中的值指定为 1024 倍-Xss。事实上,您只能创建线程数量的一小部分,并且进程的虚拟内存大小使这一点变得显而易见(取自进程的 vmmap 输出):

Stack                  0000000800004000-0000040800000000 [  4.0T] rw-/rwx SM=NUL  thread 23
Stack                  0000040800000000-0000040800003000 [   12K] rw-/rwx SM=PRV  thread 23
Run Code Online (Sandbox Code Playgroud)

每个堆栈 4TB?这会很痛苦(这是你之前要求的):

一旦我们将其调整为 1024 倍,我们就会在第二次运行中获得相同数量的线程 - 您可以在输出中更清楚地看到这些数字,并且它们会随着请求的大小线性缩放:

for i in {1..8}; do echo "${i}G:"; java -XX:ThreadStackSize=${i}m -XX:+PrintFlagsFinal -version 2>&1 | grep ' ThreadStack'; done
1G:
     intx ThreadStackSize                          := 1048576                             {pd product}
2G:
     intx ThreadStackSize                          := 2097152                             {pd product}
3G:
     intx ThreadStackSize                          := 3145728                             {pd product}
4G:
     intx ThreadStackSize                          := 4194304                             {pd product}
5G:
     intx ThreadStackSize                          := 5242880                             {pd product}
6G:
     intx ThreadStackSize                          := 6291456                             {pd product}
7G:
     intx ThreadStackSize                          := 7340032                             {pd product}
8G:
     intx ThreadStackSize                          := 8388608                             {pd product}
Run Code Online (Sandbox Code Playgroud)

因此,看起来 using-Xss<size>仅当您正在寻找 < 1GB 的堆栈大小时才有用;如果您正在寻找 > 1GB 的堆栈大小,那么您可以使用 明确指定它-XX:ThreadStackSize

弄清楚溢出。解析Xss选项的代码:

julong long_ThreadStackSize = 0;
ArgsRange errcode = parse_memory_size(tail, &long_ThreadStackSize, 1000);
Run Code Online (Sandbox Code Playgroud)

然后,在一场恒星木偶 表演中,它做到了

FLAG_SET_CMDLINE(intx, ThreadStackSize,
                          round_to((int)long_ThreadStackSize, K) / K);
Run Code Online (Sandbox Code Playgroud)

ie 将 向下转换long为 an int,然后将其传递给round_to。这需要一个Register值,该值是 64 位 VM 上的 64 位值。所以据我所知,您开始的值是:

0x80000000
Run Code Online (Sandbox Code Playgroud)

获取符号扩展为:

0xFFFFFFFF80000000
Run Code Online (Sandbox Code Playgroud)

该值除以 1024 (0x400):-

0x3FFFFFFFE00000 == 18,014,398,507,384,832
Run Code Online (Sandbox Code Playgroud)

这样您就可以看到前面脚本中的 2GB 值来自哪里。

我记录了一个错误。源中需要的更改是为了保持计算正确而不是(int)long_ThreadStackSize应该的。(Register)long_ThreadStackSize