About Java 10 Docker support

Java 10, which is a milestone in terms of numbers, has been released, and I think that the Java community is enjoying the spring after a long time and is frightened by the upcoming update race.

The new features of Java 10 have been introduced on various blogs, but I did a little research on Docker support that I was personally interested in.

Java 10 Release Notes: http://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html

About Docker, there are about 3 correspondences, but here is what you are interested in.

Improve docker container detection and resource configuration usage
The following changes have been introduced in JDK 10 to improve the execution and configurability of Java running in Docker containers:

JDK-8146115 Improve docker container detection and resource configuration usage
The JVM has been modified to be aware that it is running in a Docker container and will extract container specific configuration information instead of querying the operating system. The information being extracted is the number of CPUs and total memory that have been allocated to the container. The total number of CPUs available to the Java process is calculated from any specified cpu sets, cpu shares or cpu quotas. This support is only available on Linux-based platforms. This new support is enabled by default and can be disabled in the command line with the JVM option:

-XX:-UseContainerSupport

In addition, this change adds a JVM option that provides the ability to specify the number of CPUs that the JVM will use:

-XX:ActiveProcessorCount=count

This count overrides any other automatic CPU detection logic in the JVM.

When the JVM runs inside the Doocker container, I used to look at the OS settings without looking at the Docker container settings, but Java 10 improved that. So, I hurriedly created a Docker image with Java 10 and tried it to see what it actually looks like.

The Docker image was created with this script. https://gist.github.com/c9katayama/634b14ea65f3910448e0c93b4637a1c1 You can create an Ubuntu Docker image by downloading the above script, setting the dockerhub user name appropriately, and then executing the Linux version tar.gz of the Oracle JDK in the same folder.

CPU count report

I also created Java8 (162) and Java9 (9.0.4) images for comparison. The execution environment is AmazonLinux 2017.09, and the EC2 instance is m4.xlarge (4CPU / mem 16G).

Java8 I ran the following script.

//docker container start
docker run -it c9katayama/java8:162

//java code implementation
echo 'public class T{ public static void main(String[] args){ System.out.println("CPU:"+Runtime.getRuntime().availableProcessors()); }}' > T.java

//Compile and run
javac -cp . T.java && java -cp . T

//result
CPU:4

In Java8, when the number of processors was not specified in the Docker container, 4 which is the same as the number of instances could be obtained. Next, try setting the number of CPUs to 1 when starting the Docker container (to be exact, assign the second processor).

//docker container start
docker run --cpuset-cpus 1 -it c9katayama/java8:162

//Java code implementation and execution (omitted)

//result
CPU:1

Unexpectedly, I was able to get the correct number of CPUs allocated to the Docker container. Java9 Next is Java 9. You can use JShell from Java9, so use this.

docker run -it c9katayama/java9:9.0.4
jshell
jshell> System.out.println("CPU:"+Runtime.getRuntime().availableProcessors())
CPU:4

This has 4 CPUs without any problem. Next, try allocating CPU

docker run --cpuset-cpus 1 -it c9katayama/java9:9.0.4
jshell
jshell> System.out.println("CPU:"+Runtime.getRuntime().availableProcessors())
CPU:1

Again, contrary to expectations, I got the correct number of CPUs.

Java10 Java 10 is my favorite, but as it has been,

docker run -it c9katayama/java10:10
->CPU:4
docker run  --cpuset-cpus 1 -it c9katayama/java10:10
->CPU:1

was. So, as far as I verified, the --cpuset-cpus option seems to be working from Java8, so

int threadNum = Runtime.getRuntime().availableProcessors() /2;

It seems that the behavior of code like this will not change.

cpus option (additional note)

From Docker1.13, there seems to be an option called -cpus, and I also checked this.

Java8

docker run --cpus 1.0 -it c9katayama/java8:162
//abridgement
CPU:4

Java9

docker run --cpus 1.0 -it c9katayama/java9:9.0.4
//abridgement
CPU:4

Java10

docker run --cpus 1.0 -it c9katayama/java10:10
//abridgement
CPU:1

Regarding the --cpus option, it seems that Java 9 has not been supported correctly, and in Java 10, the one assigned to the Docker container can be taken correctly.

Memory amount report

Next, let's check the amount of memory. The method to use is

Runtime.getRuntime().maxMemory();

is. For the sake of clarity, we will summarize with / without memory amount specification.

No memory amount specified

Java8

docker run -it c9katayama/java8:162
echo 'public class T{ public static void main(String[] args){ System.out.println("MEM:"+Runtime.getRuntime().maxMemory()/1024/1024+"M"); }}' > T.java
javac -cp . T.java && java -cp . T
MEM:3568M

Java9

docker run -it c9katayama/java9:9.0.4
jshell> System.out.println("MEM:"+Runtime.getRuntime().maxMemory()/1024/1024+"M")
MEM:4014M

Java10

docker run -it c9katayama/java10:10
jshell> System.out.println("MEM:"+Runtime.getRuntime().maxMemory()/1024/1024+"M")
MEM:4014M

For Java8, 9 and 10, it seems that the default value (1/4 of the OS memory amount) is set based on the OS memory amount, which is as expected (I'm worried that Java8 is a little small) )

With memory amount specified

Next, try specifying the memory specification (512M). Java8

docker run -m 512m -it c9katayama/java8:162
echo 'public class T{ public static void main(String[] args){ System.out.println("MEM:"+Runtime.getRuntime().maxMemory()/1024/1024+"M"); }}' > T.java
javac -cp . T.java && java -cp . T
MEM:3568M

Java9

docker run -m 512m -it c9katayama/java9:9.0.4
jshell> System.out.println("MEM:"+Runtime.getRuntime().maxMemory()/1024/1024+"M")
MEM:4014M

Java10

docker run -m 512m -it c9katayama/java10:10
jshell> System.out.println("MEM:"+Runtime.getRuntime().maxMemory()/1024/1024+"M")
MEM:123M //Is decreasing

Java8 and Java9 brilliantly pass through the setting of Docker container, but Java10 seems to set the maximum memory amount based on the specification of Docker container.

About the impact

As far as I've verified, there seems to be no problem with the CPU (at least for the --cpuset-cpus option), but the --cpus option seems to be better reviewed as it behaves differently. Especially when using paralellStream (), the number of threads used depends on the number of processors, which may affect performance.

Regarding memory, there may be a problem in the case where 1. the amount of memory was specified in the Docker container 2. the Java VM has no memory option and it seems to have worked well so far.

I asked @sugarlife (https://twitter.com/sugarlife) to tell me that there is a JVM option (-XX: -UseContainerSupport) that eliminates this behavior. https://twitter.com/sugarlife/status/976355508343881729

When I actually passed it to the JVM, it returned to the behavior before Java 9.

docker run -m 512m -it c9katayama/java10:10
echo 'public class T{ public static void main(String[] args){ System.out.println("MEM:"+Runtime.getRuntime().maxMemory()/1024/1024+"M"); }}' > T.java
javac -cp . T.java 
java -XX:-UseContainerSupport -cp . T  //Run with options
MEM:4014M

In any case, I think it's okay to review the memory area once in Java 10.

Docker resource allocation: https://docs.docker.com/config/containers/resource_constraints/#configure-the-default-cfs-scheduler

Recommended Posts

About Java 10 Docker support
About Docker
About Docker
About Java interface
[Java] About Java 12 features
SonarQube Java 11 support
[Java] About arrays
Java support period
Something about java
About Java features
About Java threads
Java8, 9, 10 support period
[Java] About interface
About Java class
About Java arrays
About java inheritance
About interface, java interface
/ n \ n docker java
About Docker capacity
About List [Java]
About java var
About Java literals
About Java commands
About Java log output
About Java functional interface
Java, about 2D arrays
A story about Java 11 support for Web services
About [Java] [StreamAPI] allMatch ()
About Java StringBuilder class
[Java] About Singleton Class
About Java method binding
Find out about Docker
[Java] About anonymous classes
About method splitting (Java)
[Java Silver] About initialization
About Java Array List
About inheritance (Java Silver)
About Java String class
About Java access modifiers
About Java lambda expressions
Personal summary about Java
Lombok's Java 9+ support story
[Java] About enum type
All about Java programming
Summary of Java support 2018
About java abstract class
A note about Java GC
What I researched about Java 8
About an instance of java
What I researched about Java 6
[Gradle] About Java plug-in tasks
Oracle Java 8 on Docker Ubuntu
About Java variable declaration statements
What I researched about Java 9
[Java] About try-catch exception handling
About Java class loader types
[Java Silver] About equals method
[Java] About String and StringBuilder
About Docker Hub pull limit
What I researched about Java 7
About Alibaba Java Coding Guidelines