Reputation: 35296
When I used a program, on our linux cluster, that needed the number of cpus on my machine I was surprised to see that the number detected by java (ncpus=1) was different from nproc (ncpus=32)
nproc
32
so I wrote a minimal program to test this:
import java.lang.management.*;
public class Test {
public static void main(String args[]) throws Exception {
OperatingSystemMXBean op = (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
System.err.println("A - CPUS: " + op.getAvailableProcessors());
System.err.println("B - CPUS: " + java.lang.Runtime.getRuntime().availableProcessors());
}
}
so using JAVA 11
$ java -version && javac Test.java && java Test
openjdk version "11.0.8-internal" 2020-07-14
OpenJDK Runtime Environment (build 11.0.8-internal+0-adhoc..src)
OpenJDK 64-Bit Server VM (build 11.0.8-internal+0-adhoc..src, mixed mode)
A - CPUS: 1
B - CPUS: 1
but using JAVA 8 returns the expected result:
$ java -version && javac Test.java && java Test
openjdk version "1.8.0_112"
OpenJDK Runtime Environment (Zulu 8.19.0.1-linux64) (build 1.8.0_112-b16)
OpenJDK 64-Bit Server VM (Zulu 8.19.0.1-linux64) (build 25.112-b16, mixed mode)
A - CPUS: 32
B - CPUS: 32
why do I get two different number for two different java versions ? why does java 11 looks wrong ?
Upvotes: 2
Views: 636
Reputation: 1751
As someone mentioned in the comments, if you are running inside a container, or if CPU limits have been set in the OS using cgroups, then that could explain why nproc
returns a different value than Runtime.getRuntime().availableProcessors()
.
Which would basically mean that the behavior you're seeing in Java 11 is a feature, not a bug.
To check if CPU limits are set, look at the files in the following area:
/sys/fs/cgroup
For example on Debian/Ubuntu:
> cat /sys/fs/cgroup/cpu.max
max 100000
That value is "nano CPUs" so divide by 100000
to get the actual number of CPUs.
My understanding is that more recent JVMs are now "container-aware" in that they will respect cgroup settings in JDK APIs such as the Runtime
class.
From my testing, JDK 11 and later will show the cpu.max setting from Runtime.getRuntime().availableProcessors()
I've read online somewhere that the container-aware settings have been backported to Java 8 but my testing using the OpenJDK 8 image does not show this to be true.
My testing shows that Java 8 does not respect the cgroup CPU limits, while Java 11 does.
In case it helps, here's a project to demonstrate the differences: https://github.com/scottrobey/StackOverflowExamples/tree/master/docker-testing/cpu-limits
And here are the results of testing with Java 8 and Java 11:
# image: openjdk:8
java-test-1 | openjdk version "1.8.0_342"
java-test-1 | OpenJDK Runtime Environment (build 1.8.0_342-b07)
java-test-1 | OpenJDK 64-Bit Server VM (build 25.342-b07, mixed mode)
java-test-1 | JVM Available CPUs: 2
java-test-1 | cgroup cpu.max: 100000 100000
java-test-1 | nproc: 2
# image: openjdk:11
java-test-1 | openjdk version "11.0.16" 2022-07-19
java-test-1 | OpenJDK Runtime Environment 18.9 (build 11.0.16+8)
java-test-1 | OpenJDK 64-Bit Server VM 18.9 (build 11.0.16+8, mixed mode, sharing)
java-test-1 | JVM Available CPUs: 1
java-test-1 | cgroup cpu.max: 100000 100000
java-test-1 | nproc: 2
Check the Java 11 release notes and you'll see information about Cgrousp v2 support also called "Container awareness": https://www.oracle.com/java/technologies/javase/11all-relnotes.html
Upvotes: 2