我可以设置 Java VM 可用的线程/CPU 数量吗?

Posted

技术标签:

【中文标题】我可以设置 Java VM 可用的线程/CPU 数量吗?【英文标题】:Can I set the number of Threads/CPUs available to the Java VM? 【发布时间】:2016-02-16 20:13:30 【问题描述】:

我想限制 Java VM 可用的线程/进程数,类似于设置可用内存的方式。我希望能够将其指定为仅使用 1 个线程或任意数量。

注意:我无法在代码中设置它,因为我想限制的代码是一个我无法修改源代码的库。因此,它必须是对虚拟机级别施加的硬性上限。 (或者,如果您可以对可以覆盖库的应用程序本身施加线程限制?)

注意 2:这样做的目的是进行性能测试,以限制我要测试的库,看看当它可以访问不同数量的 CPU/线程时它的性能如何。

谢谢!

【问题讨论】:

你为什么要这样做? @Matt Timmermans 一些 JVM 在无法再创建一个线程时的行为是抛出 OutOfMemoryError' 您可以在一个 CPU 上拥有任意数量的线程。几乎所有时候,如果一个库试图创建一个线程并且不能,那么它将无法工作。如果您有一个创建一大堆线程的库,那么它可能有一个配置选项来设置它将使用的最大数量。 为什么要限制Java线程的数量?如果您想要进行与实际场景有任何相似之处的性能测试,您应该限制 Java VM 可用的内核数量(可能还有亲和力)。 这是操作系统的任务,而不是 JVM。如果你使用linux,可以使用专用的cgroup。 【参考方案1】:

JVM 中的 CPU 限制问题在 Java 10 中得到解决,并从 build 8u191 向后移植到 Java 8:

-XX:ActiveProcessorCount=2

【讨论】:

适用于默认线程池中的线程数。但是不能限制 CPU。【参考方案2】:

如果您使用的是 linux,只需将 java 启动器包装在 numactl / taskset 命令中。这允许 JVM 生成任意数量的线程,但将它们安排在固定数量的物理内核上。

其他操作系统也可以使用类似的工具。

【讨论】:

不幸的是,HotSpot JVM 在 Linux 上不尊重taskset(错误JDK-6515172)。 JVM 仍然会认为所有处理器都可用,从而导致过多的 GC 线程、编译器线程以及应用程序和库可能依赖的 Runtime.availableProcessors() 的值,例如默认 ForkJoinPool 的大小也是基于availableProcessors @apangin,编译器/GC线程可以调整,其余的似乎也不是不可逾越的障碍。可能仍然足以进行测试。【参考方案3】:

尝试以 Windows 用户的“亲和力”运行您的程序。

例如:而不是运行“java Test”,您应该运行: 当您需要 1 个核心时,“启动 /affinity 1 java Test”; 当您需要 2 个核心时,“启动 /affinity 3 java Test”; …… 使用的参数应遵循以下格式:

https://blogs.msdn.microsoft.com/santhoshonline/2011/11/24/how-to-launch-a-process-with-cpu-affinity-set/

您可以使用“System.out.println(Runtime.getRuntime().availableProcessors()); 来检查。

【讨论】:

【参考方案4】:

在 JDK 8u191 之前,没有 VM 标志或属性来控制 Java 可用的 CPU 数量,即 Runtime.availableProcessors() 返回的值。

在 Windows 和 Solaris 上,如果您设置了进程关联掩码,它也会影响Runtime.availableProcessors()。这在 Linux 上不起作用,请参阅 JDK-6515172。

还有一个使用 LD_PRELOAD 补丁或操作系统级别技巧的 Linux 解决方法,请参阅this question 中的详细信息。

更新

自 JDK 8u121 起,JVM 现在尊重 Linux 上的任务集,请参阅 JDK-6515172 从 JDK 8u191 开始,有一个 JVM 标志 -XX:ActiveProcessorCount=nn 来覆盖 JVM 可见的 CPU 数量,请参阅 JDK-8146115

【讨论】:

这个答案不再有效,因为 -XX:ActiveProcessorCount 标志也被反向移植到 OpenJDK 8。【参考方案5】:

我建议您可以实现并安装自己的 SecurityManager,它会跟踪创建的线程数并在达到最大值时抛出错误。

根据对this question 的接受回答,每次创建/启动新线程时都会检查带有“modifyThreadGroup”目标的RuntimePermission

更新

SecurityManager 的第一种方法可能是这样的:

class MySecurityManager extends SecurityManager

    private final int maxThreads;

    private int createdThreads;

    public MySecurityManager(int maxThreads)
    
        super();
        this.maxThreads=maxThreads;
    

    @Override
    public void checkAccess(Thread t)
    
        // Invoked at Thread *instantiation* (not at the start invokation).
        super.checkAccess(t);

        // Synchronized to prevent race conditions (thanks to Ibrahim Arief) between read an write operations of variable createdThreads:
        synchronized(this)
        
            if (this.createdThreads == this.maxThreads)
            
                throw new Error("Maximum of threads exhausted");
            
            else
            
                this.createdThreads++;
            
        
    

当然,必须进行进一步的测试以确保始终允许系统线程。并且保持这个算法不会在线程结束时减少计数。

【讨论】:

'alphaloop' 的'accepted' 下面的答案说实际上不可能通过SecurityManager 来做到这一点?无论如何,我不想只监控它,我想指定一个上限,以便将其限制在特定级别,以便我可以看到库在不同条件下的性能。 我明白了。我已经在我的答案中包含并测试了一个限制线程数的 SecurityManager 。你可以从这个开始,然后继续测试。 此代码可能会导致竞争条件扰乱检查和增量。至少,使用this.createdThreads > this.maxThreads 而不是直接等效。充其量,重构代码以使用更好的并发机制。 @Ibrahim Arief 对。我已经更新了我的帖子,尽管新的同步会稍微降低性能。谢谢。 @SimonLogic 事实上,我没有(阅读最后的注释)。为了获得准确、最新的活动线程计数,我想必须包含一个 ReferenceQueue。【参考方案6】:

作为最后的手段,我可​​以在任务管理器中将 Java VM 的关联设置为仅使用 1 个(或更多)CPU。 (这当然仍然允许 1 个 CPU 上的多个线程,但如果没有其他人有更好的想法,这可能是最接近我想要的东西)

【讨论】:

【参考方案7】:

为了测试这一点,我使用了更高的 java 版本(java 版本“14.0.2”2020-07-14)和这个选项:

-XX:ActiveProcessorCount=2

但是,这不会限制 JVM 使用超过 2 个处理器。如果可用,它会使用更多。

根据我的理解(docs)和实验,这会影响线程池中线程数的计算等。但是 NOT 是否对 CPU 数量施加任何限制分配给进程。

限制 cpu 使用的一种方法是在 docker 容器内运行 java 进程并使用

"--cpus"

泊坞窗选项。

这是我特别在 Mac OS 上使用的,目前没有支持的实用程序来限制 CPU。

另一种方法是使用特定于操作系统的实用程序,例如 taskset / isolcpus / cgroups(在 Linux 上可用)。

所以如果问题是限制java进程使用的cpu,那么ActiveProcessorCount选项本身就是useless的。在 docker 容器中运行它,或者使用操作系统特定的包装器来处理 CPU 或其他资源的分配/限制。

【讨论】:

以上是关于我可以设置 Java VM 可用的线程/CPU 数量吗?的主要内容,如果未能解决你的问题,请参考以下文章

线程池应该设置多少核心线程数——Java多线程系列学习笔记

线程池应该设置多少核心线程数——Java多线程系列学习笔记

强制多个线程在可用时使用多个 CPU

线程数,射多少更舒适?

不能使用 CPU 中总可用线程的一半以上

java线程池如何合理的设置大小