Best Practices: Java Memory Arguments for Containers
In this article, we will discuss the possible JVM arguments that can be used to specify the Java heap size and the best option to choose.
Join the DZone community and get the full member experience.
Join For FreeWhen running your Java application in physical servers, you would have been using ‘-Xmx’ JVM argument to specify the Java heap size. If you are porting your application to Containers, you might be wondering how to configure Java heap size in the container’s world? Are there any best practices? In this article, we will discuss the possible JVM arguments that can be used to specify the Java heap size and the best option to choose.
There are 3 different options to specify the maximum Java heap size in containers. They are:
1. -XX:MaxRAMFraction, -XX:MinRAMFraction
2. -XX:MaxRAMPercentage, -XX:MinRAMPercentage
3. -Xmx
Let us discuss these JVM arguments, their merits, and their shortcomings.
The Ultimate Java Expert Certification Bundle.*
*Affiliate link. See Terms of Use.
1. -XX:MaxRAMFraction, -XX:MinRAMFraction
Supported Version
‘-XX:MaxRAMFraction’, ‘-XX:MinRAMFraction’ JVM arguments are supported from only Java 8 update 131 to Java 8 update 190. So, if you are using any other version of JDK, you cannot use this option.
How Does it Work?
Say you have allocated 1 GB of memory to your container, then if you configure -XX:MaxRAMFraction=2, then approximately ~512GB (i.e., 1/2 of 1GB) will be allocated to your Java heap size.
If you are going to use the ‘-XX:MaxRAMFraction’ JVM argument, make sure to pass these two additional JVM arguments as well ‘-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap’. If you pass these two JVM arguments, then JVM will derive the heap size value from the container’s memory size. Otherwise, it will derive the heap size value from the underlying host’s memory size.
# docker run -m 1GB openjdk:8u131 java -XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-XX:MaxRAMFraction=2 -XshowSettings:vm -version VM settings:
Max. Heap Size (Estimated): 494.94M
Here you can see when the docker container’s memory is set to ‘1GB’ (i.e., -m 1GB) and ‘-XX:MaxRAMFraction=2. Based on this setting, JVM allocates Max heap size to be 494.9MB (approximately half the size of 1GB).
Note: Both ‘-XX:MaxRAMFraction’ and ‘-XX:MinRAMFraction’ are used to determine the maximum Java heap size. JDK development team could have given a better name than ‘-XX:MinRAMFraction’. This name makes us think, ‘-XX:MinRAMFraction’ argument is used to configure minimum heap size. But it’s not true. To learn more about their difference, read this article.
What Are its Limitations?
Here are the drawbacks to this approach.
a. Say if you want to configure 40% of the docker’s memory size, then we must set ‘-XX:MaxRAMFraction=2.5’. When you pass 2.5 as the value, JVM will not start. It is because ‘-XX:MaxRAMFraction’ can take only integer values and not decimal values. See the below example where JVM is failing to start.
# docker run -m 1GB openjdk:8u131 java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
-XX:MaxRAMFraction=2.5 -XshowSettings:vm -version VM
Improperly specified VM option 'MaxRAMFraction=2.5'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
b. In this option, your Java application’s heap size will be derived from the container’s memory size (because it is fraction basis). Say suppose if your application requires a 1GB heap size for optimal performance and if the container is configured to run with a memory size that is less than 1GB, then your application will still run. Still, it will suffer from poor performance characteristics.
c. This argument has been deprecated in modern Java versions. It is only supported from Java 8 update 131 to Java 8 update 190.
2. -XX:MaxRAMPercentage, -XX: MinRAMPercentage
Supported Version
‘-XX:MaxRAMPercentage’, ‘-XX: MinRAMPercentage’ JVM arguments are supported from Java 8 update 191 and above. So, if you are running on older JDK versions, you can’t use this JVM argument.
How Does it Work?
Say you have allocated 1 GB of memory to your container, then if you configure -XX:MaxRAMPercentage=50, then approximately 512GB (i.e., 1/2 of 1GB) will be allocated to your Java heap size.
xxxxxxxxxx
# docker run -m 1GB openjdk:10 java -XX:MaxRAMPercentage=50 -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 494.94M
Using VM: OpenJDK 64-Bit Server VM
You can see when the docker container’s memory is set to ‘-m 1GB’ and ‘-XX:MaxRAMPercentage=50’. Based on this setting, JVM allocates Max heap size to be 494.9MB (approximately half of 1GB).
Note: Both ‘-XX:MaxRAMPercentage’ and ‘-XX:MinRAMPercentage’ are used to determine the maximum Java heap size. JDK development team could have given a better name than ‘-XX:MinRAMPercentage’. This name makes us think, ‘-XX:MinRAMPercentage’ argument is used to configure minimum heap size. But it’s not true. To learn more about their difference, read this article.
Note: In several articles on the internet, it’s mentioned that you need to pass the ‘-XX:+UseContainerSupport’ JVM argument when you are passing ‘-XX:MaxRAMPercentage’, ‘-XX:InitialRAMPercentage’, ‘-XX:MinRAMPercentage’. Actually, that’s not true. ‘-XX:+UseContainerSupport’ is passed by default argument in the JVM. So, you don’t need to configure it explicitly.
What Are the Limitations?
Here are the limitations to this approach.
a. This argument is not supported in the older versions of Java. It is only supported by Java 8 update 191.
the container’s memory size will derive b. In this option, your Java application’s heap size (because it is percentage basis). If your application requires a 1GB heap size for optimal performance and if the container is configured to run with a memory size that is less than 1GB, then your application will still run. Still, it will suffer from poor performance characteristics.
3. -Xmx
Supported Version:
‘-Xmx’ is supported in all versions of Java
How Does it Work?
Using the ‘-Xmx’ JVM argument, you specify fine-grained specific size such as 512MB, 1024MB.
Here you can see the -Xmx to supported in non-container (traditional Physical server world):
# java -Xmx512m -XshowSettings:vm -version
VM settings:
Max. Heap Size: 512.00M
Ergonomics Machine Class: client
Using VM: OpenJDK 64-Bit Server VM
Here you can see the -Xmx to supported in java 8 update 131 version in the container world:
# docker run -m 1GB openjdk:8u131 java -Xmx512m -XshowSettings:vm -version VM
VM
settings:
Max. Heap Size: 512.00M
Ergonomics Machine Class: client
Using VM: OpenJDK 64-Bit Server VM
Here you can see the -Xmx to supported in java 10 version in the container world:
# docker run -m 1GB openjdk:10 java -Xmx512m -XshowSettings:vm -
version
VM settings:
Max. Heap Size: 512.00M
Using VM: OpenJDK 64-Bit Server VM
What Are the Limitations?
a. If you are going to allocate ‘-Xmx’ more than the container’s memory size, then your application will experience ‘java.lang.OutOfMemoryError: kill process or sacrifice child.’
Best Practices
- Irrespective of what option you use for configuring heap size (i.e., -XX:MaxRAMFraction, -XX:MaxRAMPercentage, -Xmx), always make sure you allocate at least 25% more memory to your container (i.e. ‘-m’) than your heap size value. Say you have configured -Xmx value to be 2GB, then configure the container’s memory size at least to be 2.5GB. Do this even if your Java application is the only process going to run on the container. Because a lot of engineer thinks Java application will not consume more than -Xmx value. That is not true. Besides heap space, your application needs space for Java threads, Garbage collection, metaspace, native memory, socket buffers. All these components require additional memory outside the allocated heap size. Besides that, other small processes (like APM agents, Splunk scripts, etc.) will also require memory. To learn more about them, watch this quick ‘JVM Memory’ video clip.
- If you are running *only your Java application* within the container, then set initial heap size (i.e., using either one of ‘-XX:InitialRAMFraction’, ‘-XX:InitialRAMPercentage’, -Xms) to the same size as max heap size. Setting initial heap size and max heap has certain advantages. One of them is: you will incur lower Garbage Collection pause times. Because whenever heap size grows from the initial allocated size, it will pause the JVM. It can be circumvented when you set initial and max heap sizes to be the same. Besides that, if you have an under-allocated container’s memory size, then JVM will not even start (which is better than experiencing OutOfMemoryError when transactions are in flight).
- In my personnel opinion, I prefer to use -Xmx option than -XX:MaxRAMFraction, -XX:MaxRAMPercentage options to specify Java Heap size in the container world, for the following reasons:
- I do not want the container’s size to determine my java application’s heap size. Your body’s size should decide whether you will wear a ‘small’ or ‘medium’ or ‘large’ size T-shirt, not the other way around. You do not want to fit-in a 6-foot man with a ‘small’ size T-shirt. Memory size plays a THE KEY ROLE in deciding your application’s performance. It influences your garbage collection behavior and performance characteristics. You do not want that factor to be decided by your container’s memory setting.
- Using ‘-Xmx’, I can set fine-grained/precision values like 512MB, 256MB.
- -Xmx is supported on all java versions.
- It would help if you studied whether the containers' new settings impact your application’s garbage collection and performance characteristics. To study the garbage collection behavior, you can use free tools like GCeasy, IBM GC & Memory Visualizer, HP JMeter.
Opinions expressed by DZone contributors are their own.
Comments