Before You Begin
This 15-minute tutorial shows you how to set processor and
memory constraints in a Docker container and test if Java
recognizes the constraints. It also shows you how to generate OutOfMemoryError
exceptions programmatically and test Docker container
integration with Java.
Background
Docker is a popular technology for containerizing Java Virtual Machine (JVM) applications. When deployed, it offers consistent environments for development, deployment, and proper isolation between applications.
In Java Platform, Standard Edition Development Kit 9 (JDK 9) and earlier, the JVM didn’t recognize the resource constraints in a Docker container, because it fetched processor and memory configurations from the underlying host instead of the Docker container. This issue was resolved in JDK 10, and the JVM now automatically recognizes the constraints set by a Docker container, tunes itself, and presents the available resources to the Java application that is running inside the Docker.
It is important to note that this issue is currently being addressed in JDK 8.
To help you understand how Docker integration was enhanced in JDK 10, the examples in this tutorial show commands for JDK 8 and JDK 10.
What Do You Need?
- Understanding of Docker Container technology
- Latest version of Oracle Linux
- Docker for Oracle Linux (This tutorial requires Docker version 17.06.2-ol, build d02b7ab.)
- JDK 8 or Open JDK 8 (This tutorial requires version 8u181 or earlier.)
- JDK 10 or Open JDK 10
docker_java_obe.zip
Set
the Processor Constraints to Test JDK 8 Docker Container
Integration
- Open a Linux terminal and log in as a root user.
- Browse to the
docker_java_obe/core_checkdirectory. - Check the number of available runtime processors for the
host machine.
# lscpu | grep "CPU"In this example, the number of available processors is 4.
CPU op-mode(s): 32-bit, 64-bit CPU(s): 4 On-line CPU(s) list: 0-3 CPU family: 6 Model name: Intel(R) Xeon(R) CPU E5-2699 v3 @ 2.30GHz CPU MHz: 2295.021 NUMA node0 CPU(s): 0-3
- Open the
Dockerfilefile in the Linux text editor.# vi Dockerfile - To ensure that the Docker container uses JDK 8 to build the
image, press Insert and set the
FROM openjdk:field to8.After you edit the file, the content should look similar to this:
FROM openjdk:8; ADD CoreCheck.java . WORKDIR . RUN javac CoreCheck.java CMD ["java", "-showversion", "CoreCheck" ]
- Save the file and exit.
- Build a Docker image named
corecheck8.# docker build -t corecheck8 .The output should look similar to this sample.
- Run the
corecheck8Docker image.# docker run corecheck8In this example, the number of available processors is 4.
openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-1~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode) 4
- In the Docker container, run the
corecheck8Docker image with the processor share set to 2048, to provide two CPU time allocations for the Java application inside the Docker.# docker run --cpu-shares 2048 corecheck8In this example, the total number of available processors is 4.
openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-1~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode) 4
Ideally, the output should have been 2, which is half of the available processor time (2048/1024). JDK 8 doesn’t recognize the Docker container limits because the JVM reads the processor configuration from the OS of the host machine, not from the Docker container.
Set
the Processor Constraints to Test JDK 10 Docker Container
Integration
- Open the
Dockerfilefile in the text editor.# vi Dockerfile - To ensure that the Docker container uses JDK 10 to build
the image, press Insert and set the
FROM openjdk:field to10.After you edit the file, the content should look similar to this:
FROM openjdk:10 ADD CoreCheck.java . WORKDIR . RUN javac CoreCheck.java CMD ["java", "-showversion", "CoreCheck" ]
- Save the file and exit.
- Build a Docker image named
corecheck10.# docker build -t corecheck10 .The output should look similar to this sample.
- Run the
corecheck10Docker image.# docker run corecheck10In this example, the number of available processors is 4.
openjdk version "10.0.2" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2+13-Debian-1) OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-1, mixed mode) 4
- In the Docker container, run the
corecheck10Docker image with the processor share set to 2048, to provide two CPU time allocations for the Java application inside the Docker.# docker run --cpu-shares 2048 corecheck10In this example, the total number of available processors is 2, and it’s half of the available processor time (2048/1024).
openjdk version "10.0.2" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2+13-Debian-1) OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-1, mixed mode) 2
JDK 10 is a Docker-aware version and recognizes the processor limits within the Docker container. The JVM reads the processor configuration from the Docker container, not from the host machine.
Set
the Memory Constraints to Test JDK 8 Docker Container
Integration
- Browse to the
docker_java_obe/memory_checkdirectory. - Check the total available physical memory of the host
machine.
# freeIn this example, the total available physical memory is 14745848 KB.
total used free shared buff/cache available Mem: 14745848 323896 11484064 17232 2937888 14235580 Swap: 7167984 0 7167984
- Open the
Dockerfilefile in the text editor.# vi Dockerfile - Press Insert and set the
FROM openjdk:field to8.After you edit the file, the content should look similar to this:
FROM openjdk:8 ADD MemoryCheck.java . WORKDIR . RUN javac MemoryCheck.java CMD ["java", "-showversion", "MemoryCheck" ]
- Save the file and exit.
- Build a Docker image named
memorycheck8.# docker build -t memorycheck8 .The output should look similar to this sample.
- Run the
memorycheck8Docker image.# docker run memorycheck8In this example, the available memory is 3357540352 B, which is equal to approximately one-fourth of the physical memory ((14745848*1024)/4).
openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-1~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode) 3357540352
- In the Docker container, run the
memorycheck8image with the memory limit set to 100 MB.# docker run --memory 100m memorycheck8In this example, the available memory is 3357540352 B.
openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-1~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode) 3357540352
Ideally, the output should have been approximately 50 MB (100/2), which is half of the Docker memory limit that you set. JDK 8 doesn’t recognize the Docker container limits because the JVM reads the memory configuration from the OS of the host machine, not from the Docker container.
Set
the Memory Constraints to Test JDK 10 Docker Container
Integration
- Open the
Dockerfilefile in the text editor. - Press Insert and set the
FROM openjdk:field to10.After you edit the file, the content should look similar to this:
FROM openjdk:10 ADD MemoryCheck.java . WORKDIR . RUN javac MemoryCheck.java CMD ["java", "-showversion", "MemoryCheck" ]
- Save the file and exit.
- Build a Docker image named
memorycheck10.# docker build -t memorycheck10 .The output should look similar to this sample.
- Run the
memorycheck10Docker image.# docker run memorycheck10In this example, the available memory is 3776970752 B, which is equal to approximately one-fourth of the physical memory ((14745848*1024)/4).
openjdk version "10.0.2" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2+13-Debian-1) OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-1, mixed mode) 3776970752
- In the Docker container, run the
memorycheck10image with the memory limit set to 100 MB.# docker run --memory 100m memorycheck10In this example, the available memory is 50724864 B (approximately 50 MB), which is half of the value of the Docker memory limit (100 MB) that you set.
openjdk version "10.0.2" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2+13-Debian-1) OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-1, mixed mode) 50724864
JDK 10 is a Docker-aware version and recognizes the memory limits within the Docker container. The JVM reads the memory configuration from the Docker container, not from the host machine.
# vi Dockerfile
Generate
OutOfMemoryError to Test JDK 8 Docker Container Integration
- Browse to the
docker_java_obe/oom_checkdirectory. - Open the
Dockerfilefile in the text editor.# vi Dockerfile - Press Insert and set the
FROM openjdk:field to8.After you edit the file, the content should look similar to this:
FROM openjdk:8 ADD TestOOM.java . WORKDIR . RUN javac TestOOM.java CMD ["java","-showversion","TestOOM" ]
- Save the file and exit.
- Build a Docker image named
oom8.# docker build -t oom8 .The output should look similar to this sample.
- Run the
oom8Docker image.# docker run oom8In this example, the output prints
All good,which indicates that the JVM did not run out of heap memory space.openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-1~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode) All good
- In the Docker container, run the
oom8Docker image with the memory constraint set to 100 MB, to exhaust the heap memory space.# docker run --memory 100m oom8In this example, the output prints nothing.
openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-1~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
Ideally, the JVM should have thrown an
OutOfMemoryErrorexception. Instead, it claimed more memory and went over the Docker limits. Because the JVM didn’t throw an exception, the Docker ended the Java process inside the container. JDK 8 doesn't recognize the memory constraints set inside a Docker container.
Generate
OutOfMemoryError to Test JDK 10 Docker Container Integration
- Open the
Dockerfilefile in the text editor.# vi Dockerfile - Press Insert and set the
FROM openjdk:field to10.After you edit the file, the content should look similar to this:
FROM openjdk:10 ADD TestOOM.java . WORKDIR . RUN javac TestOOM.java CMD ["java","-showversion","TestOOM" ]
- Save the file and exit.
- Build a Docker image named
oom10.# docker build -t oom10 .The output should look similar to this sample.
- Run the
oom8Docker image.# docker run oom10In this example, the output prints
All good,which indicates that the JVM did not run out of heap memory space.openjdk version "10.0.2" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2+13-Debian-1) OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-1, mixed mode) All good
- In the Docker container, run the
oom10Docker image with the memory constraint set to 100 MB, to exhaust the heap memory space.# docker run --memory 100m oom10In this example, the output prints
OutOfMemoryError.openjdk version "10.0.2" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2+13-Debian-1) OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-1, mixed mode) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at TestOOM.main(TestOOM.java:3)JDK 10 is a Docker-aware version. The JVM recognizes the memory limits inside a Docker container and throws an
OutOfMemoryErrorexception when it runs out of heap memory space.
Configure
and Test JDK 10 Docker Container Integration