Pierre
Pierre

Reputation: 35296

java.lang.Runtime.getRuntime().availableProcessors() : difference beween java 8 (ncpus=32) and java 11 (ncpus=1)

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

Answers (1)

Scott Robey
Scott Robey

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

Related Questions