JVM Memory Allocation in Docker Container

Evren Yortuçboylu
3 min readJan 10, 2018

--

In memory of good old days…

The logo that takes you to 90's

JVM allocates some memory automatically depending on the system memory by default. Even if it is running inside a Docker container, JVM will (try to) allocate some portion of whole system memory.

docker run java java -XshowSettings:vm -versionVM settings:
Max. Heap Size (Estimated): 444.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM

But Docker containers can be limited to use a maximum amount of memory and any container using more memory will be killed.

This is a danger for our container created by JVM. What if container has less memory than JVM’s allocated memory? It will be killed immediately.

docker run -m 300MB java java -XshowSettings:vm -versionVM settings:
Max. Heap Size (Estimated): 444.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM

As you can see, JVM still allocates 444.5 Mb of RAM which will result of this container to be killed.

Setting Max Memory Allocation for JVM
We can also configure JVM to use min and max memory with -Mxm and -Mxs options. In this case, JVM will not use a larger memory than max.

Actually this seemingly solves our problem. If we set both container memory and JVM max memory, we will be safe.

But, someday if i want to change container memory allocation, i will need to change JVM options too. Also, i need to make an educated guess on how much maximum memory to set for JVM. Because heap memory is not the only memory required for JVM. If i set a higher value, then container will be in danger again!

UseCGroup options here to rescue
With the help new CGroup memory limit options, we can make JVM to allocate as much memory as she can depending on the container memory limit, not the entire system memory!

docker run -m 300MB java java \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseCGroupMemoryLimitForHeap \
-XX:MaxRAMFraction=1
-XshowSettings:vm \
-version
VM settings:
Max. Heap Size (Estimated): 267.00M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM

As you can see, now, JVM knows that she is running inside a container which has only 300 Mbytes of memory and adjusts herself accordingly.

This is especially better than setting a hardcoded memory limit for JVM upfront if you run a container orchestration such as Kubernetes or Mesos. You will only care about container memory and JVM will adjust herself.

Testing on local machine
In my experiments, i started with 300Mbyte of container memory and started to increase gradually to see how JVM will allocate memory. At some point, JVM got stuck at 1.74 Gbyte of memory allocation. Even i grant 4Gb of memory for container, JVM still could allocate only 1.74 Gb of memory.

After several hours of desperation, i figured out that it was something related to my local Docker vm memory limited was set to 2Gb. This is why JVM could never exceed 1.74 Gb memory allocation. After i set to 4Gb, JVM was able to allocate much more memory as expected.

Docker max memory limit

Happy hacking.

--

--