Can I set the number of Threads/CPUs available to the Java VM?
Solution 1
The problem of CPU limits in JVM was solved in Java 10 and is backported to Java 8 from build 8u191:
-XX:ActiveProcessorCount=2
Solution 2
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.
Solution 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:
You can use "System.out.println(Runtime.getRuntime().availableProcessors()); to check.
Solution 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
Solution 5
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.
Martin K
Updated on February 15, 2021Comments
-
Martin K over 3 years
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!
-
Martin K over 8 yearsThe 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.
-
Little Santi over 8 yearsI 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.
-
Ibrahim Arief over 8 yearsThis 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. -
Little Santi over 8 years@Ibrahim Arief Right. I've updated my post, although a new synchronization will reduce performance a little. Thanks.
-
apangin over 8 yearsUnfortunately HotSpot JVM does not respect
taskset
on Linux (bug JDK-6515172). JVM will still think all processors are available, resulting in excessive number of GC threads, Compiler threads and the value ofRuntime.availableProcessors()
that applications and libraries may depend on, e.g. the size of default ForkJoinPool is also based onavailableProcessors
. -
the8472 over 8 years@apangin, Compiler/GC threads can be tuned, the rest doesn't seem like insurmountable obstacles either. Might still be good enough for testing.
-
Jasper Siepkes over 4 yearsThis answer is no longer valid since the
-XX:ActiveProcessorCount
flag was also backported to OpenJDK 8. -
Little Santi about 4 years@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.
-
Khanna111 about 3 yearswill work for the thread count in the default thread pools. Will not work to limit the CPUs though.
-
keturn about 2 yearsThe 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?