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.
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.
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.
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.
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) )
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.
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