Saturday, February 6, 2021

Java Heap Sizing in a Container: Quickly and Easily

Oracle Java Exam Preparation, Oracle Java Tutorial and Material, Oracle Java Certification, Core Java

We have seen that Java has made improvements to identify the memory based on a running environment i.e. either a physical machine or a Container (docker). The initial problem with java was that It wasn't able to figure out that it was running in a container and It used to capture the memory for whole hardware where the container was running.

Now a Java Program running in a container is able to identify the cgroup limit and assign the memory (heap) according to that, (If we do not specify the min and max heap size, which we used to define earlier). So we can run our java program in a container and utilize hardware memory properly, but can we very sure that Java program is using heap size according to cgroup definition?

We have a solution to this problem as XshowSettings:category. This is a handy HotSpot JVM flag (option for the Java launcher java) is the -XshowSettings option. This option is described in the Oracle Java launcher description page as follows:

-XshowSettings:category

Shows settings and continues. Possible category arguments for this option include the following:

all

Shows all categories of settings. This is the default value.

locale

Shows settings related to locale.

properties

Shows settings related to system properties.

vm

Shows the settings of the JVM.

[These types of flags described in the Java tech note - as https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html ]

The showSettings flag provides lots of details about the program running in that JVM, however, our interest is to find out the memory utilization so we will stick to the argument as -> -XshowSettings:vm -version

Let's verify this by running a simple container program without specifying the cgroup flag (which informs java that the program is running in a Java container)

(I set a container memory of 100MB and my JVM sets a max heap of 3.24G)

[root java-8]# docker run -m 100MB oracle-server-jre java -XshowSettings:vm -version

VM settings:

    Max. Heap Size (Estimated): 3.24G

    Ergonomics Machine Class: server

    Using VM: Java HotSpot(TM) 64-Bit Server VM

java version "1.8.0_181"

Java(TM) SE Runtime Environment (build 1.8.0_181-b13)

Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

We can clearly see that Java Heap is capturing the heap available to the hardware however the docker size was specified to 100 mb only, Now let's tell Java that the program is running inside the container and see the results.

With java version 1.8.0_181 (including unlock experimental version and Cgroup limit with a specification of memory 1GB MB) with only jdk Docker 

[root]# docker run -m 1GB oracle-server-jre java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version

VM settings:

    Max. Heap Size (Estimated): 228.00M

    Ergonomics Machine Class: server

    Using VM: Java HotSpot(TM) 64-Bit Server VM

java version "1.8.0_181"

Java(TM) SE Runtime Environment (build 1.8.0_181-b13)

Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

Oracle Java Exam Preparation, Oracle Java Tutorial and Material, Oracle Java Certification, Core Java
The JVM was able to detect the container has only 1GB and set the max heap to 228M, which is almost 1/4th of the total available size.

We can see that the Java Heap is set to 1/4th of the docker size and rest of the 3/4th memory is utilized by docker, but what if our container doesn't require 3/4 memory,  

If the container or the program configuration doesn't need that much of memory then this is not the correct utilization of memory. The ratio between Java heap and Container size is defined inside JVM code, so the question is - can we configure this ratio?? 

Yes, Java did a very good thing by providing another option to change the ratio of container memory and java heap size. Java introduced another flag as MaxRAMFraction ( the default value to this flag is 4, so heap size takes 1/4th of container size and we can change it by defining it explicitly)

we have defined RAM fraction =2, Which settles heap size on almost 50% of the total.

[root]# docker run -m 1GB oracle-server-jre java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=2 -XshowSettings:vm -version

VM settings:    

    Max. Heap Size (Estimated): 455.50M

    Ergonomics Machine Class: server

    Using VM: Java HotSpot(TM) 64-Bit Server VM

java version "1.8.0_181"

Java(TM) SE Runtime Environment (build 1.8.0_181-b13)

Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

The heap size reaches approximately to 50% of the total container size.

Now we can change the ratio as well according to our Java program comfort level, but this leads to another thought that is is safe to run a container, if we define this ratio to 1? (however JVM won't capture whole container memory and It leaves some memory to run some other container programs like container debug shell (docker exec) and diagnostics, OS processes, etc. If the program or container needs more memory under load then it will kill the container, so I feel defining the least ratio

-XX:MaxRAMFraction=2

 seems safe(ish), if we want to customize the default JVM ratio which seems to be the safest ratio.

Related Posts

0 comments:

Post a Comment