【问题标题】:What is threshold limit number of threads that a JVM can createJVM可以创建的线程的阈值限制数是多少
【发布时间】:2016-09-21 02:38:16
【问题描述】:

编辑:

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

sysctl kern.num_taskthreads 是:

kern.num_taskthreads: 2048

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

但是还是很奇怪,不管我怎么改-Xss args,结果总是2031-Xss args 似乎只适用于我现在不确定的主线程。

原问题:

我进行了一项测试,以了解一个 JVM 可以创建多少个线程。而当我调整 JVM 参数时,-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();
        }
    }
}

以及操作系统信息:

  • 型号名称:MacBook Pro
  • 型号标识符:MacBookPro11,4
  • 处理器名称:英特尔酷睿 i7
  • 处理器速度:2.2 GHz
  • 处理器数量:1
  • 内核总数:4
  • 二级缓存(每核):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)

结果:

ulimit -a:

sysctl kern.num_threads:

kern.num_threads: 10240

【问题讨论】:

  • 听起来您遇到了kern.num_taskthreads 限制,而不是整体线程限制,它限制了单个进程的线程数。这个值不是可调的,不费吹灰之力。
  • 根据设计,任何限制都会有特定于平台的实现细节。无论您得到什么数字或答案,都不要期望它们在跨平台或 Java 版本中保持稳定。
  • @Petesh 你是对的。还有一个问题,linux有这样的限制吗?
  • Linux 本身没有每个进程的线程限制。最大进程数将有效地为用户设置线程限制; /proc/sys/kernel/threads-max 是总体限制。
  • @Petesh 我刚刚使用 VM args 进行了另一项测试,-XX:ThreadStackSize=4g,当我创建 24 个线程时出现错误;但是当我使用-Xss4g 时,我在创建 2032 个线程时遇到了错误。你对这些差异有什么想法吗?

标签: java multithreading jvm


【解决方案1】:

我在我的linux jvm 1.8.0_92上测试了一下,和你说的一样,发现是这样的:

What is the difference between -Xss and -XX:ThreadStackSize?

还有预言机页面:

http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

上面写着:

以下示例以不同的单位将线程堆栈大小设置为 1024 KB:

   -Xss1m
   -Xss1024k
   -Xss1048576

This option is equivalent to -XX:ThreadStackSize

【讨论】:

  • 是的,它是这么说的,但结果不同。这很奇怪。
  • 是的,关于difference between ...的文章说windows上的jvm是可以的
  • @little.Pig 我在 linux 中尝试了 Xss2g 和 XX:ThreadStackSize=2g,两者都无法创建 vm,但第一个显示 intx ThreadStackSize := 18014398507384832,另一个显示 `intx ThreadStackSize := 2147483648`。这比我想象的要复杂。
  • 在我的 64 位 linux VM 上,我可以使用 -Xss10239m 创建 JVM,但不能使用 -Xss10240m;我可以使用-XX:ThreadStackSize=6m 创建JVM,但不能使用-XX:ThreadStackSize=7mulimit -astack size (kbytes, -s) 10240。这很奇怪。
【解决方案2】:

所有这些东西都是特定于操作系统的——在 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}

当我们将此与 -XX:ThreadStackSize 进行比较时,我们有不同的画面:

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

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

Stack                  0000000800004000-0000040800000000 [  4.0T] rw-/rwx SM=NUL  thread 23
Stack                  0000040800000000-0000040800003000 [   12K] rw-/rwx SM=PRV  thread 23

每个堆栈 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}

所以,看起来使用-Xss<size> 仅在您寻找-XX:ThreadStackSize 明确指定它。

找出溢出。 parses the Xss option的代码:

julong long_ThreadStackSize = 0;
ArgsRange errcode = parse_memory_size(tail, &long_ThreadStackSize, 1000);

然后在恒星木偶剧it does

FLAG_SET_CMDLINE(intx, ThreadStackSize,
                          round_to((int)long_ThreadStackSize, K) / K);

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

0x80000000

sign extended 获取到:

0xFFFFFFFF80000000

除以 1024 (0x400):-

0x3FFFFFFFE00000 == 18,014,398,507,384,832

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

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

【讨论】:

  • 我也使用了HotSpot VM,只是安装了DCE VM。我运行了您的代码并得到了相同的结果。但我还是无法理解。似乎发生了一些溢出。我试图阅读邮件列表中提到的源代码:What the difference between -Xss and -XX:ThreadStackSize is?。而且我没有发现溢出发生在哪里。
  • 我添加了几段关于溢出是如何发生的 - 这是一个符号扩展错误,通过将 long 值转换为 int,然后通过符号扩展将其变成一个巨大的值。
  • 我对@9​​87654351@不是很熟悉,请问在哪里可以找到round_to函数的定义?这似乎是代码中一个非常明显的错误......
  • Register 类型是什么意思?它是一个无符号长值吗?
  • defined in this file 用于 x86 平台。 Register 类在以 32 位代码运行时对应于 32 位值,在以 64 位代码运行时对应于 64 位值。错误在于调用round_to - 在调用例程创建Register 时,显式转换为int 后跟C++ 的符号扩展是破坏值的原因,这与round_to的实际代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-04
  • 2018-02-23
  • 2013-06-24
  • 2019-06-22
  • 1970-01-01
  • 2021-11-22
  • 2017-05-09
相关资源
最近更新 更多