Java memory and container
Properly limiting the JVM’s memory usage (Xmx isn’t enough)
This whole section taken from Properly limiting the JVM’s memory usage (Xmx isn’t enough), by Matt Rasband (3 Jun, 2017) medium1
The JVM is known to be greedy with memory and is infamously difficult to tune. This is pretty apparent when you set your -Xmx
and find that the application is exceeding that value, and even more apparent when running a JVM based application in Docker — because the JVM can see the host’s memory in many cases. The error can manifest any number of ways such as higher latencies due to garbage collection or memory swapping, and in some cases (such as in Docker) getting OOMed.
Most solutions to this issue suggest just setting -Xmx256m
and calling it a day. Unfortunately, that only limits the max heap size, not the total amount of memory the JVM will utilize as you need to account for metaspace, class space, stack size, and more. You can read a bit more in depth here. In short, the actual maximum utilized memory by your application is a function (credit to the link above):
Of course the JVM itself needs some space to do its thing as well, so there is still a bit of overhead there too.
Long story short, just setting -Xmx
is only going to defer when your application shows symptoms of using more memory than expected. Depending on usage volume the symptom can be deferred much longer, but eventually the symptoms of improper JVM tuning will be visible. In cases such as usage in Docker, we had seen cascading restarts due to service dependencies (only things like a central config and service discovery service really caused this).
Sensible default for Spring boot applications
Using Cloud Foundry JVM Memory Calculator
The Java buildpack memory calculator determines values for JVM memory options with the goal of enabling an application to perform well while not exceeding the total memory available in a container (which results in the application being killed).2
The buildpack provides the following inputs to the memory calculator:2
the total memory available to the application,
an optional head room (a percentage of the total memory available, default 0) which should not be allocated,
an estimate of the number of threads that will be used by the application,
an estimate of the number of classes that will be loaded,
the type of JVM pool used in the calculation ('permgen' for Java 7 and 'metaspace' for Java 8 and later),
any JVM options specified by the user.
The java buildpack in Cloud Foundry calculates the memory settings for a java process. It has a hard job because it only has one input (the container memory limit) and it needs to come up with at least 5 numbers for the JVM. To do this it uses a standalone memory calculator program. We downloaded the memory calculator and used it to drive some tests on memory usage in a Spring Boot application. 3
Here are the command line options generated by the default settings with some typical container memory limits:3
Container
-Xmx (Heap)
-XX:MaxMetaspaceSize (Metaspace)
-Xss (Stack)
128m
54613K
64M
568K
256m
160M
64M
853K
512m
382293K
64M
995K
1g
768M
104857K
1M
Running a JVM in a Container Without Getting Killed
The JDK 8u131 has backported a nice feature in JDK 9, which is the ability of the JVM to detect how much memory is available when running inside a Docker container.4
In Java 10 there is improved container integration. No need to add extra flags, the JVM will use 1/4 of the container memory for heap.5
Java 10 obsoletes the -XX:MaxRAM
parameter, as the JVM will correctly detect the value.
You can still use the -XX:MaxRAMFraction=1
option to squeeze all the memory from the container.5
But it can be risky if your container uses off heap memory, as almost all the container memory is allocated to heap. You would have to either set -XX:MaxRAMFraction=2
and use only 50% of the container memory for heap, or resort to Xmx
.5
Java 10 and docker memory reporting
For java 10 to get the correct memory, it also needs a later version of docker:
Wrong: Max. Heap Size (Estimated): 15.69G
Correct: Max. Heap Size (Estimated): 247.50M
Allow more flexibility in selecting Heap % of available RAM
Three new flags based on percentages, from 0.0 to 100.0 6
-XX:MaxRAMPercentage
-XX:MinRAMPercentage
-XX:InitialRAMPercentage
MaxRAM vs mx
-Xmx specifies the precise upper limit for the heap. It is the preferred way to set the heap size.
-XX:MaxRAM does not define the heap size directly. Instead this parameter overrides the actual amount of physical RAM when calculating the heap limits basing on ergonomics.
If -Xmx is set, MaxRAM is never used. Otherwise the maximum heap size is estimated as7
See also
1. Properly limiting the JVM’s memory usage (Xmx isn’t enough), by Matt Rasband (3 Jun, 2017) medium ↩
2. Java Buildpack Memory Calculator github ↩
3. spring-boot-memory-blog, by Ben Hale github ↩
4. Running a JVM in a Container Without Getting Killed, by Carlos Sanchez (31 May, 2017) Carlos Sanchez's Weblog ↩
5. Running a JVM in a Container Without Getting Killed II, by Carlos Sanchez (21 Jun, 2018) Carlos Sanchez's Weblog ↩
6. Allow more flexibility in selecting Heap % of available RAM, by Bob Vandette (30 Aug, 2017) openjdk ↩
7. What is the difference between xmx and MaxRAM JVM parameters?, by apangin (7 Jan, 2019) stackoverflow ↩
Last updated