51

I would like to limit the number of threads/processes available to the Java VM, similar to how you set the available memory. I would like to be able to specify it to just use 1 thread, or an arbitrary number.

NOTE: I cannot set it in the code, as the code that I would like to limit is a library where I cannot modify the source. So it must be a hard cap imposed on the level of the virtual machine. (Or if you could impose a thread limit on the application itself that could override libraries?)

NOTE2: The purpose of this is a performance test, to throttle a library I want to test, to see how well it would perform when it has access to a different number CPUs/Threads.

Thanks!

Seki
  • 11,135
  • 7
  • 46
  • 70
Martin K
  • 765
  • 1
  • 6
  • 15
  • 1
    Why do you want to do this? – Sotirios Delimanolis Nov 15 '15 at 18:33
  • Sounds like an XY problem. What are you REALLY trying to do? Why do you wanna limit the number of threads? – Vince Nov 15 '15 at 18:34
  • What do you want to happen if the library tries to create too many threads? – Matt Timmermans Nov 15 '15 at 18:40
  • I want to test the performance of the library when using X number of threads. Starting from 1. – Martin K Nov 15 '15 at 18:42
  • 1
    @Matt Timmermans Some JVMs' behaviour when failed to create one more thread is to throw an `OutOfMemoryError'`. – Little Santi Nov 15 '15 at 18:44
  • Well the library should run even when there is only 1 CPU/thread, so I don't tink that should be a problem. – Martin K Nov 15 '15 at 18:45
  • 5
    You can have as many threads as you want on one CPU. Almost all the time, if a library tries to create a thread and can't, then it won't work. If you have a library that creates a whole bunch of threads, then it likely has a configuration option to set the maximum number that it will use. – Matt Timmermans Nov 15 '15 at 18:47
  • @Matt Timmermans I think the way the library is written is that it will try to use as many threads as it can, but if it cannot generate any more threads it will still run. The problem is that the it has very poor documentation so there is no obvious way to specify this. – Martin K Nov 15 '15 at 18:55
  • 2
    why do you want to limit the amount of *Java* threads? If you want to do performance tests that have any kind of resemblance to real-world scenarios, you should be limiting the amount (and possibly affinity) of cores available to the Java VM. – snemarch Nov 15 '15 at 19:14
  • @snemarch Yes, that is pretty much what I would like to do. Is there a Java VM parameter/option that specifies that? – Martin K Nov 15 '15 at 19:15
  • 2
    It is a task for the OS, not the JVM. If you use linux, you can use a dedicated cgroup. – fge Nov 15 '15 at 19:23
  • Limited googling shows no JVM parameters but hacks to do it form within Java - I'd personally just use the features of the host OS when launching the JVM and call it a day. – snemarch Nov 15 '15 at 19:23

7 Answers7

48

The problem of CPU limits in JVM was solved in Java 10 and is backported to Java 8 from build 8u191:

-XX:ActiveProcessorCount=2
mirekphd
  • 4,799
  • 3
  • 38
  • 59
5

To test this, I used the a later java version (java version "14.0.2" 2020-07-14) and this option:

-XX:ActiveProcessorCount=2

However, this would not restrict the JVM from using more than 2 processors. It would use more if available.

As per my understanding (of the docs) and experimentation, this affects the calculation of the number of threads in a thread pool etc. But does NOT apply any restrictions on the number of cpus allocated to the process.

One way to restrict the cpu usage was to run the java process inside of a docker container and using the

"--cpus"

docker option.

This is what I use especially on Mac OS that has currently no supported utility to restrict cpus.

Another way is to use a OS specific utility such as taskset / isolcpus / cgroups (available on Linux).

So if the question is to limit the cpus used by the java process, then the ActiveProcessorCount option is useless on its own. Run it inside the docker container or use an OS specific wrapper that deals with allocation / restriction of cpu or other resources.

Khanna111
  • 3,627
  • 1
  • 23
  • 25
4

Before JDK 8u191, there was no VM flag or a property to control number of CPUs available to Java, i.e. what Runtime.availableProcessors() returns.

On Windows and Solaris if you set the process affinity mask, it will also affect Runtime.availableProcessors(). This did not work on Linux though, see JDK-6515172.

There is also a work around for Linux using LD_PRELOAD patch or a OS-level trick, see details in this question.

UPDATE

  • JVM now respects taskset on Linux since JDK 8u121, see JDK-6515172
  • Since JDK 8u191, there is a JVM flag -XX:ActiveProcessorCount=nn to override the number of CPUs visible to the JVM, see JDK-8146115
apangin
  • 92,924
  • 10
  • 193
  • 247
3

If you're on linux simply wrap the java launcher in a numactl / taskset command. That allows the JVM to spawn any number of threads but schedules them on a fixed amount of physical cores.

Similar tools are available for other OSes too.

Community
  • 1
  • 1
the8472
  • 40,999
  • 5
  • 70
  • 122
  • Unfortunately HotSpot JVM does not respect `taskset` on Linux (bug [JDK-6515172](https://bugs.openjdk.java.net/browse/JDK-6515172)). JVM will still think all processors are available, resulting in excessive number of GC threads, Compiler threads and the value of `Runtime.availableProcessors()` that applications and libraries may depend on, e.g. the size of default ForkJoinPool is also based on `availableProcessors`. – apangin Nov 16 '15 at 15:55
  • @apangin, Compiler/GC threads can be tuned, the rest doesn't seem like insurmountable obstacles either. Might still be good enough for testing. – the8472 Nov 16 '15 at 18:29
  • The bug apangin linked is marked as resolved for Java 9, and if I'm reading it correctly, was backported to Java 1.8 as well. So hopefully this works for currently supported Java versions? – keturn May 10 '22 at 21:54
3

Try to run your program with "affinity" for windows user.

For Example: instead of running "java Test", you should run: "start /affinity 1 java Test" when you need 1 core; "start /affinity 3 java Test" when you need 2 core; .... The parameter used should follow this form:

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

You can use "System.out.println(Runtime.getRuntime().availableProcessors()); to check.

Zhihui Shao
  • 377
  • 3
  • 5
1

I suggest you could implement and install your own SecurityManager which tracks the number of created threads and throws an Error when the maximum is reached.

According to the accepted answer to this question, a RuntimePermission with "modifyThreadGroup" target is checked every time a new thread is created/started.

Update

A first approach of the SecurityManager could be like this:

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++;
            }
        }
    }
}

Of corse, further testing must be done to gurantee that system threads are always allowed. And remain that this algorithm does not decrement the count when a thread ends.

Community
  • 1
  • 1
Little Santi
  • 8,563
  • 2
  • 18
  • 46
  • The answer below the 'accepted' one by 'alphaloop' says that it is actually not possible to do it via SecurityManager? In any case I didn't want to just monitor it, I wanted to specify a cap so that it is throttled to a specific level so that I can see how well the library performs under different conditions. – Martin K Nov 15 '15 at 19:05
  • I see. I've included in my answer and tested myself a SecurityManager that limits the number of threads. You could start by this, and go on on testing. – Little Santi Nov 15 '15 at 19:35
  • This code has the potential for a race conditions to mess up with the check and increment. At the very least, use `this.createdThreads > this.maxThreads` instead of direct equivalency. At best, refactor the code to use better concurrency mechanisms. – Ibrahim Arief Nov 16 '15 at 15:10
  • @Ibrahim Arief Right. I've updated my post, although a new synchronization will reduce performance a little. Thanks. – Little Santi Nov 16 '15 at 15:27
  • @SimonLogic In fact, I do not (read the note at the end). To achieve an accurate, up-to-date count of live threads, I guess a ReferenceQueue must be included. – Little Santi Apr 01 '20 at 16:18
0

As a last resort, I could set the Java VM's affinity in task manager to only use 1 (or more) CPU(s). (this will of course still allow multiple threads on 1 CPU, but it's probably the closest thing to what I wanted if noone else has any better ideas)

Martin K
  • 765
  • 1
  • 6
  • 15