【问题标题】:Maximum number of threads in a JVM?JVM中的最大线程数?
【发布时间】:2011-10-11 13:32:33
【问题描述】:

Java虚拟机最多可以维护多少线程?

我在最初的问题中没有解释这一点,但我正在尝试对 JVM 进行基准测试,并想尝试看看它可以同时维护多少个线程。

可以选择在循环中创建线程直到抛出异常,但是,我想知道是否有更好的方法来做到这一点。

【问题讨论】:

  • 很大程度上取决于底层操作系统
  • 哪个JVM,哪个操作系统,多少内存,在什么条件下?很难看出这样一个笼统的问题会有什么用处。
  • 超出您的合理使用范围。

标签: java


【解决方案1】:

您的操作系统和硬件配置会有一些限制。

要增加并发线程的数量,您应该降低默认堆栈大小java -Xss 64k

  • Oracle 32 位 JVM 将 default to 320kb 每个线程的堆栈大小。
    • 对于具有 2gb 可寻址内存的 32 位 JVM,这将为您提供最多 6.5k 线程。
  • Oracle 64 位 JVM 将 default to 1M 每个线程的堆栈大小。
    • 对于每 GB 内存,使用默认值可以获得 1024 个线程。
  • 仅适用于 Linux:
    • ulimit -a 将为您提供用户进程和内存的配置限制
    • 在 linux cat /proc/sys/kernel/pid_max 中您只会获得 32k 个唯一 PID - 最多 32k 个进程。
    • 你只会得到 255k 线程cat /proc/sys/kernel/threads-max

【讨论】:

  • 大声笑...单个线程肯定不会使用 512kb,您在这里混淆了很多术语
  • @specializt 对于 1.5 的 JVM,它是 512kb,根据 Oracle hotspot faq on threads and oom,它现在是 1.6+,对于 Linux 和 Windows 上的 32 位 jvm,默认值为 320kb。
  • @specializt 对于 64 位 JVM,它当然默认为 1M。因此,如果您坚持默认设置,每 GB 内存只能获得 1024 个线程。
  • You can reduce your stack size by running with the -Xss option. For example: java -server -Xss64k 和:64k is the least amount of stack space allowed per thread——这没有多大意义,因为分配粒度可能因系统而异:msdn.microsoft.com/en-us/library/windows/desktop/ms686774.aspx 但是......嘿......我猜 sun/ oracle 开发人员不是“擅长 WINAPI”:)
  • @specializt 我不明白你的意思是什么?对于 Windows 系统上的 JVM 中的堆栈大小不确定,对我来说似乎超出了范围,在 oracle 文档中提到 Note that on some versions of Windows, the OS may round up thread stack sizes using very coarse granularity. 在我的第一个答案中包含使用较小堆栈大小的可能性,64kb根据您链接的文章,似乎是典型的系统对windows的分配粒度。
【解决方案2】:

编写一个循环来创建新线程直到它崩溃是确定的方法。您很可能会看到性能在它真正消亡之前已经严重下降。

我不知道 JVM 中是否有任何配置参数或其他内置限制。我在实践中从未遇到过限制。当然,你迟早会耗尽内存,也许还有其他资源。

我怀疑线程数量本身没有限制,而是与线程相关的资源有限制。也就是说,您可能会看到,如果所有线程都只运行一个包含几个字节数据的小类,那么您可以拥有 10,000 个线程,但是当每个线程都有一个包含 1000 万个字符串的数组时,这个数字会迅速下降。

【讨论】:

    【解决方案3】:

    如果有限制,将由操作系统而不是 jvm 施加

    【讨论】:

    • 实际上,每一个可以想象的、确定性的软件都会对所有东西都有严格的限制,包括 JVM。永远。 x86_64 并没有改变这一点——事实上,大多数软件都会在 4GiB 左右出现计划外(尚未被发现)的限制,原因很简单:primitivesint 一样仍然存在并在任何地方使用;)。如果想编写不受这些问题影响的软件,就必须使用BigInteger 之类的——绝对无处不在,即使是循环索引。
    【解决方案4】:

    真正的问题不应该是您可以创建多少线程,而是有多少线程可以高效运行。线程太多会导致抖动,计算时间太少和更少。

    首先,问题是,你的线程要活多久。短活线程几乎不值得付出努力。另一方面,大型计算非常有意义。

    其次,每个线程会消耗多少内存。将每个线程所需的内存量除以可用内存量。您不应创建比这更多的线程。

    第三,你有多少 CPU 可用。您不应创建比 CPU 更多的线程。实际上,您应该考虑至少比线程数少一个。在具有 4 个 CPU 的 Windows 笔记本电脑上,如果需要高效处理,则线程数不应超过 3 个。

    最后,你的线程是做什么的。如果它读取和写入硬盘驱动器,那么您可以拥有比 CPU 数量更多的线程,因为它必须等待设备响应。确定线程数时考虑以下方法:

    public static int recommendedThreadCount()
    {
        int mRtnValue = 0;
        Runtime runtime = Runtime.getRuntime();
        long maxMemory = runtime.maxMemory();
        long mTotalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        int mAvailableProcessors = runtime.availableProcessors();
    
        long mTotalFreeMemory = freeMemory + (maxMemory - mTotalMemory);
        mRtnValue = (int)(mTotalFreeMemory/4200000000l);
    
        int mNoOfThreads = mAvailableProcessors-1;
        if(mNoOfThreads < mRtnValue) mRtnValue = mNoOfThreads;
    
        return mRtnValue;
    }
    

    【讨论】:

    • 我不同意第三点关于将线程数限制为CPU数的观点。坦率地说,这根本没有意义,特别是如果线程不是 CPU 密集型并且正在等待大量等待。
    • 没错,但你到处都能看到那种糟糕的建议。如果您的线程受 CPU 限制,那么拥有比内核更多的线程也无济于事。但是如果它们是 I/O 绑定的,那么你可以拥有比内核更多的线程。这是您需要根据工作负载调整的参数。
    【解决方案5】:

    百万

    好吧,如果使用 Project Loom 中的 virtual threads 技术,将获得数百万美元,该技术正在为 Java 的未来版本开发。

    在业界更普遍地称为fibers,Project Loom 下的虚拟线程运行在我们已经在 J​​ava 中拥有的“真实”平台/内核线程之上。许多虚拟线程映射到每个平台/内核线程。

    虚拟线程提供非常便宜的阻塞。当您的后台线程上的任务执行文件 i/o、网络调用、数据库访问、日志记录等时,您的代码会阻塞,等待响应。 Project Loom 技术检测到这种阻塞,“停放”(搁置)该虚拟线程,并分配另一个虚拟线程以继续其在平台/内核线程上的工作。这种停车和切换非常快。因此,线程化 Java 应用程序的性能通常会得到显着提升。

    因此,主流计算硬件上的 JVM 将能够支持数百万个线程。

    注意事项:

    • 虽然虚拟线程使阻塞变得便宜,但那些便宜的线程可能正在做昂贵的工作,例如使用大量内存。因此,在这种情况下,您可能需要对任务进行一些限制。
    • 虚拟线程适用于涉及阻塞的代码,这在面向业务的应用程序中很常见。但是,如果线程工作是CPU-bound,例如处理视频,那么您应该使用有限数量的“真实”平台/内核线程而不是虚拟线程。

    使用虚拟线程就像切换ExecutorService 的实现一样简单:

    ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor() ;
    

    有关详细信息,请参阅this 2021-01-15 article。并观看 Ron Pressler 和其他团队成员的几个非常好的视频演示和采访。随着 Loom 的发展,研究更新的材料。

    基于早期访问 Java 18 的 Project Loom 的实验版本是 available now。团队寻求反馈。

    【讨论】:

      【解决方案6】:

      最大线程限制主要取决于硬件、操作系统和 Java 堆栈大小。

      以下因素对确定最大线程限制起着非常重要的作用:-

      1. 进程虚拟地址限制(2^32 用于32-bit 架构,2^64 用于64-bit 架构)
      2. Java栈大小(可以通过命令java -XX:+PrintFlagsFinal -version | grep -iE 'ThreadStackSize'确定
      3. Max PID 限制(可以通过命令“cat /proc/sys/kernel/pid_max”确定)
      4. 最大进程限制ulimit -u

      所以最大线程限制将是((进程虚拟地址空间/java堆栈大小),最大PID限制,最大进程限制)的最小值

      例如如果最大进程限制为2048 并且是上述所有三个因素中的最小值,那么 java 进程将无法创建更多线程。

      为了验证它,可以创建一个简单的 Java 应用程序,他/她可以在其中创建一个循环中的线程并查看它可以执行多少。

      示例:

      public class ThreadCountTest {
        private static final Object lock = new Object();
        private static int counter = 0;
          
        public static void main(String[] _args) {
          while (true) {
            new Thread(new Runnable() {
              public void run() {
                synchronized(lock) {
                  counter++;
                  System.err.println("New thread #" + counter);
                }
                while (true) {
                  try {
                    Thread.sleep(3000);
                  } catch (Exception e) {
                    e.printStackTrace();
                  }
                }
              }
            }).start();
          }
        }
      }
      

      【讨论】:

        【解决方案7】:

        最大线程数也可能受到 JVM 实现的限制,并且可能因 Java 虚拟机与另一个 Java 虚拟机而异。例如,在Jikes RVM 中,一个数组用于保存有关线程的信息(参见Jikes RVM Scheduler source code 中的第54 行)。在这种情况下,最大线程数不能超过 Java 中数组的最大大小,约为 2^32。但您更有可能在达到 2^32 个线程之前达到其他操作系统限制或硬件限制。

        【讨论】:

          猜你喜欢
          • 2016-03-30
          • 2017-06-11
          • 2019-12-09
          • 2011-04-20
          • 2016-09-14
          • 2011-07-01
          • 2011-03-27
          • 2020-11-02
          相关资源
          最近更新 更多