【问题标题】:How many threads can a Java VM support?Java VM 可以支持多少个线程?
【发布时间】:2010-10-20 07:11:01
【问题描述】:

Java VM 可以支持多少个线程?这是否因供应商而异?通过操作系统?其他因素?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    当我在运行 Trisquel linux (Ubuntu 18.04) 的 2GB 三星 AMD 处理器笔记本电脑上研究这个主题时。它可以管理 9534 个线程,然后抛出特殊的异常

    at java.base/java.lang.Thread.start0(Native Method)
    at java.base/java.lang.Thread.start(Thread.java:803)
    at Main.main(Main.java:11)
    

    代码:

    public class MultithreadingRunnable implements Runnable {
        public void run() {
            System.out.println("ThreadID " +  Thread.currentThread().getId());
        }
     }
    
    
    
    public class Main {
        public static void main(String[] ars) {
    
            for(int i = 0;i<10000;i++){
                Thread mr = new Thread(new MultithreadingRunnable());
                mr.start();
            }
        }
     }
    

    【讨论】:

    • 您能告诉我如何在我的服务器上运行它吗?我有一些奇怪的错误,想检查你提到的线程。\
    【解决方案2】:

    2017 年... DieLikeADog 类。

    新线程 #92459 线程“主”java.lang.OutOfMemoryError 中的异常:无法创建新的本机线程

    i7-7700 16GB内存

    【讨论】:

    • 课程外的答案会有所不同。我得到了 10278 和 6 GB 的内存。
    【解决方案3】:

    现代 (systemd) linux 系统的附加信息。

    有很多关于这个值的资源可能需要调整(例如How to increase maximum number of JVM threads (Linux 64bit));但是通过 systemd "TasksMax" 限制施加了一个新的限制,该限制在 cgroup 上设置了 pids.max。

    对于登录会话,UserTasksMax 默认值为内核限制 pids_max(通常为 12,288)的 33%,并且可以在 /etc/systemd/logind.conf 中覆盖。

    对于服务,DefaultTasksMax 默认值为内核限制 pids_max(通常为 4,915)的 15%。您可以通过在“systemctl 编辑”中设置 TasksMax 或更新 /etc/systemd/system.conf 中的 DefaultTasksMax 来覆盖服务

    【讨论】:

      【解决方案4】:

      最大线程数取决于以下几点:

    • 微处理器、RAM 等硬件配置。
    • 操作系统是 32 位还是 64 位
    • run 方法中的代码。如果 run 方法中的代码很大,那么单线程对象将需要更多的内存
    • 【讨论】:

        【解决方案5】:

        我知道这个问题已经很老了,但我只想分享我的发现。

        我的笔记本电脑能够处理产生25,000 线程的程序,并且所有这些线程以 2 秒的固定间隔在 MySql 数据库中写入一些数据。

        我用10,000 threads30 minutes continuously 运行这个程序,然后我的系统也很稳定,我能够进行其他正常操作,如浏览、打开、关闭其他程序等。

        使用25,000 threads 系统slows down 但它仍然响应。

        使用50,000 threads 系统stopped responding 立即我不得不手动重新启动我的系统。

        我的系统详情如下:

        Processor : Intel core 2 duo 2.13 GHz
        RAM : 4GB
        OS : Windows 7 Home Premium
        JDK Version : 1.6
        

        在运行之前我设置了 jvm 参数-Xmx2048m

        希望对你有帮助。

        【讨论】:

        • “慢下来”听起来像是在交换。
        • 谢谢。这是一台非常坚固的机器,可以正常工作近 8 年,直到突然停止启动。我刚刚在它上面安装了 Ubuntu 而不是 Windows,它又开始处理数字了 :)
        • Fried core 2 duo with ubuntu 调味料:D
        • 我在生产环境中遇到过类似的问题,我的一位同事忘记关闭线程池,所以每次执行该代码都会导致线程数增加。结果,响应非常缓慢,起初我们知道 cpu 使用率非常高,并且我们知道“vm 线程”是前 1 个 cpu 消耗线程。在 jstack 之后我们检查了 jvm 内存监视器,但没有什么特别的。最后在calalina.log中我们发现了一些异常,有太多关于“线程无法回收”的错误。我花了很多时间在上面,我想要一个方法。
        【解决方案6】:

        你可以处理任意数量的线程;没有限制。我在看电影和使用 NetBeans 时运行了以下代码,它工作正常/没有停止机器。我认为你可以保留比这个程序更多的线程。

        class A extends Thread {
            public void run() {
                System.out.println("**************started***************");
                for(double i = 0.0; i < 500000000000000000.0; i++) {
                    System.gc();
                    System.out.println(Thread.currentThread().getName());
                }
                System.out.println("************************finished********************************");
            }
        }
        
        public class Manager {
            public static void main(String[] args) {
                for(double j = 0.0; j < 50000000000.0; j++) {
                    A a = new A();
                    a.start();
                }
            }
        }
        

        【讨论】:

        • 我知道这是评论很晚了,但我相信你能够启动和运行这么多线程的原因是因为(假设正常和合理的系统配置)每个线程基本上打印出一行输出,然后没有更多存在的理由,并在不久后被杀死,从而确保资源的持续可用性。
        • @ucsunil 这不是真的。我认为您误读了代码。我刚刚尝试过,不同的线程不仅打印出第一行,还打印出它们的名称。这意味着它们是活跃的。如果你认为垃圾回收会回收未引用的线程,它不会,see here
        • 但是,不久之后,main 会在我的机器上抛出一个OutOfMemoryError,说它不能再创建任何线程。也许@AnilPal,你没有注意到。我建议在main(..) 方法中包含另一个打印语句,以便清楚地看到它在抛出错误后何时停止创建新线程。
        • 太棒了……发现了实际图灵机……无限资源。
        【解决方案7】:

        至少在 Mac OS X 10.6 32 位上,操作系统有一个限制 (2560)。检查这个stackoverflow thread

        【讨论】:

          【解决方案8】:

          绝对理论最大值通常是进程的用户地址空间除以线程堆栈大小(但实际上,如果您的所有内存都保留给线程堆栈,您不会有工作程序...)。

          因此,例如在 32 位 Windows 下,每个进程都有 2GB 的用户地址空间,给每个线程一个 128K 的堆栈大小,您会期望绝对最大值为 16384 个线程 (=2*1024*1024 / 128)。在实践中,我发现我可以在 XP 下启动大约 13,000 个。

          然后,我认为您本质上是在 (a) 是否可以管理在代码中处理那么多线程而不做明显愚蠢的事情(例如让它们都等待同一个对象然后调用 notifyAll()...),以及 (b) 操作系统是否可以。原则上,如果 (a) 的答案也是“是”,那么 (b) 的答案就是“是”。

          顺便说一句,您可以在线程的构造函数中指定堆栈大小;您不需要(也可能不应该)为此弄乱 VM 参数。

          【讨论】:

          • 所以使用 64 位操作系统。我们都使用 64 位处理器多久了?
          • 当然,我只是举一个理论与实际限制的例子。请注意,仍有大量 32 位机器(包括服务器)......
          【解决方案9】:

          在玩过 Charlie 的 DieLikeACode 类之后,看起来 Java 线程堆栈大小是您可以创建的线程数量的重要组成部分。

          -Xss 设置java线程栈大小

          例如

          java -Xss100k DieLikeADog

          但是,Java 有 Executor 接口。我会使用它,您将能够提交数千个 Runnable 任务,并让 Executor 使用固定数量的线程处理这些任务。

          【讨论】:

          • 我们可以将它命名为 DieLikeACat 吗?在你运行它之前它不会死也不会活。
          • 感谢指点Executors,大家应该多用点。但如果Runnable/Callable 确实需要连续运行,比如它必须处理通信时,它就行不通了。但它非常适合 SQL 查询。
          【解决方案10】:

          嗯,很多。

          这里有几个参数。特定的 VM 以及 VM 上通常还有运行时参数。这在某种程度上是由操作系统驱动的:底层操作系统对线程有什么支持,对线程有什么限制?如果虚拟机实际上完全使用操作系统级别的线程,那么好的旧的红色线程/绿色线程。

          什么是“支持”是另一个问题。如果你编写一个类似的 Java 程序

             class DieLikeADog {
                   public static void main(String[] argv){
                       for(;;){
                          new Thread(new SomeRunaable).start();
                       }
                   }
              }
          

          (不要抱怨语法细节太少,我正在喝第一杯咖啡)那么您当然应该期望有成百上千的线程在运行。但是创建一个线程是相对昂贵的,而且调度器的开销会变得很大;目前还不清楚您是否可以让这些线程做任何有用的事情。

          更新

          好吧,忍不住了。这是我的小测试程序,有一些修饰:

          public class DieLikeADog {
              private static Object s = new Object();
              private static int count = 0;
              public static void main(String[] argv){
                  for(;;){
                      new Thread(new Runnable(){
                              public void run(){
                                  synchronized(s){
                                      count += 1;
                                      System.err.println("New thread #"+count);
                                  }
                                  for(;;){
                                      try {
                                          Thread.sleep(1000);
                                      } catch (Exception e){
                                          System.err.println(e);
                                      }
                                  }
                              }
                          }).start();
                  }
              }
          }
          

          在 Intel 上的 OS/X 10.5.6 和 Java 6 5(参见 cmets)上,这就是我得到的

          新线程 #2547 新线程 #2548 新线程 #2549 无法创建线程:5 新线程 #2550 线程“主”java.lang.OutOfMemoryError 中的异常:无法创建新的本机线程 在 java.lang.Thread.start0(本机方法) 在 java.lang.Thread.start(Thread.java:592) 在 DieLikeADog.main(DieLikeADog.java:6)

          【讨论】:

          • 你用多少内存来启动 JVM?这很重要。
          • Java 6 更新 13,Ubuntu 8.10 32 位,4Gig ram,默认 JVM 设置 = 6318 线程。
          • 嘿,玩一下线程堆栈大小。 java -Xss100k 允许我在 Linux 中创建 19702 个线程。
          • java -Xss50k 让我获得了大约 32k 个线程。不过,这使我的 4gigs 内存最大化。我必须停止一些正在运行的进程才能在我的机器上获得足够的内存来分叉一个新进程来杀死 java ;) - 好时光。
          • 在 Windows 7 上使用 Java 7 在我的系统死机之前,我刚刚创建了 200,000 个线程。任务管理器显示了使用 8GB RAM 的进程。不过,不知道为什么它停在那里……我的电脑上有 12GB 的 RAM。所以这可能会达到其他限制。
          【解决方案11】:

          阅读 Charlie Martin 的帖子后,我很好奇堆大小是否会影响您可以创建的线程数量,结果让我彻底傻眼了。

          在 Vista Home Premium SP1 上使用 JDK 1.6.0_11,我执行了 Charlie 的测试应用程序,堆大小在 2 MB 到 1024 MB 之间。

          例如,要创建一个 2 MB 的堆,我会使用参数 -Xms2m -Xmx2m 调用 JVM。

          这是我的结果:

          2 mb --> 5744 threads
          4 mb --> 5743 threads
          8 mb --> 5735 threads
          12 mb --> 5724 threads
          16 mb --> 5712 threads
          24 mb --> 5687 threads
          32 mb --> 5662 threads
          48 mb --> 5610 threads
          64 mb --> 5561 threads
          96 mb --> 5457 threads
          128 mb --> 5357 threads
          192 mb --> 5190 threads
          256 mb --> 5014 threads
          384 mb --> 4606 threads
          512 mb --> 4202 threads
          768 mb --> 3388 threads
          1024 mb --> 2583 threads
          

          所以,是的,堆大小肯定很重要。但是堆大小和最大线程数之间的关系是成反比的。

          这很奇怪。

          【讨论】:

          • 如果每个线程都被赋予一个该大小的堆,这将是有意义的。
          • 警告:我的机器没有 2583 GB 的 RAM。或者交换。并且 JVM 不分配线程本地堆空间。所以这不可能......
          • 堆大小减少了可用于堆栈的地址空间。 256K/堆栈的地址空间是有意义的。
          • 是的,这显示了同样的东西pequenoperro.blogspot.com/2009/02/less-is-more.html
          【解决方案12】:

          我记得在一次具有数千个内核(9000 个?)的贸易展上,他曾在某台专用机器上运行他的一个应用程序,并加载了所有这些。不幸的是,我现在找不到链接(帮助?)。

          基于此,我认为可以肯定地说硬件和您的代码是限制因素,而不是 JVM。

          【讨论】:

          • 你能再看看吗?我想看看 - 听起来很有趣,并且支持函数式语言易于跨内核扩展。
          • 你能提供一个链接吗?我知道 Azul Systems 的首席工程师 Cliff Click, Jr. 在 Azul 最大的 JCA 系统(Azul Vega 3 系列 7300 型号 7380D:AzulSystems.Com/products/compute_appliance_specs.htm)上运行了 Rich Hickey 的 Ant Colony Simulation,该系统具有 864 个内核和 768 GB RAM,以及 700蚂蚁设法最大化了 700 个核心。但是 9000 个内核,这非常令人印象深刻。那是什么机器?
          • 我相信这是“蚂蚁”模拟 - 这是 Rich Hickey(Clojure 创造者)谈论这个的链接 - blip.tv/clojure/clojure-concurrency-819147。它是在一些拥有 800 多个内核的大型 Azul 系统上进行的,主要是为了展示 Clojure 在处理多核并发方面的出色表现。
          • @mikera lins 已过期。
          【解决方案13】:

          这取决于您使用的 CPU、操作系统、其他进程正在执行的操作、您使用的 Java 版本以及其他因素。在关闭机器之前,我已经看到 Windows 服务器有 > 6500 个线程。当然,大多数线程都没有做任何事情。一旦机器达到大约 6500 个线程(Java 中),整台机器就开始出现问题并变得不稳定。

          我的经验表明,Java(最新版本)可以愉快地使用计算机本身可以承载的尽可能多的线程,而不会出现问题。

          当然,您必须有足够的 RAM,并且您必须以足够的内存启动 Java,以完成线程正在执行的所有操作,并为每个线程拥有一个堆栈。任何具有现代 CPU(最近几代 AMD 或 Intel)和 1 - 2 Gig 内存(取决于操作系统)的机器都可以轻松支持具有 数千 个线程的 JVM。

          如果您需要比这更具体的答案,最好的办法是进行分析。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-27
            • 2015-05-22
            • 1970-01-01
            相关资源
            最近更新 更多