Java Tuning on OpenShift

Solution Verified - Updated

Environment

  • Red hat OpenShift Container Platform (OCP)
    • 4.x
    • 3.x
  • Red Hat build of OpenJDK:
    • OpenJDK 8u191+
    • OpenJDK 11
    • OpenJDK 17
    • OpenJDK 21

Issue

  • Java Tuning on OpenShift

Resolution

Do not set initial heap size equal to max heap size. In cloud environments (OpenShift), the goal is normally to minimize memory footprint. Initial heap size must be lower than the maximum heap size for the JVM to return memory to the OS/container. Only testing and requirements can determine the appropriate tradedoff between commit/uncommit overhead and memory footprint; however, often an arbitrary low value is fine.

The following collectors are best in cloud environments due to functionality to release memory back to the OS/container and minimize memory footprint:

  • Parallel
  • Shenandoah
  • G1 (JDK17+)

Typical JVM Options:

  1. JDK8 Serial (1 cpu/core):

-server -XX:+UseSerialGC -Xms256M -Xmx2560M -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -verbose:gc -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=N -XX:GCLogFileSize=N[K|M|G] -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/

  1. JDK8 Parallel:
-server -XX:+UseParallelGC -XX:+UseParallelOldGC -Xms256M -Xmx2560M -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -verbose:gc -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=N -XX:GCLogFileSize=N[K|M|G] -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/ 
  1. JDK 8 Shenandoah:
-server -XX:+UseShenandoahGC -XX:+AlwaysPreTouch -XX:+UseNUMA -XX:-UseBiasedLocking -XX:+UseLargePages -Xms256M -Xmx2560M -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -verbose:gc -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=N -XX:GCLogFileSize=N[K|M|G] -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/
  1. JDK17 Serial (1 cpu/core):
-server -XX:+UseSerialGC -XX:+UseLargePages -Xms256M -Xmx2560M -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Xlog:gc*,safepoint=info:file=gc.log:time,uptime:filecount=N,filesize=N[K|M|G] -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/ 

  1. JDK11/17 Parallel:
-server -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseLargePages -Xms256M -Xmx2560M -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Xlog:gc*,safepoint=info:file=gc.log:time,uptime:filecount=N,filesize=N[K|M|G] -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/ 
  1. JDK11/17 Shenandoah:
-server -XX:+UseShenandoahGC -XX:+AlwaysPreTouch -XX:+UseNUMA -XX:-UseBiasedLocking -XX:+UseLargePages -Xms256M -Xmx2560M -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Xlog:gc*,safepoint=info:file=gc.log:time,uptime:filecount=N,filesize=N[K|M|G] -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/
  1. JDK17 G1 (>2GB and >2 cores):
-server -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:MaxGCPauseMillis=500 -Xms2048M -Xmx4096M -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Xlog:gc*,safepoint=info:file=gc_%p_%t.log:time,uptime:filecount=N,filesize=N[K|M|G] -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/

Restricting Footprint

Also, it is possible to restrict the whole JVM size, i.e. footprint, not just the Heap size.

  • MaxRAM: Sets the total amount of memory used by the JVM maximum to N (where n may be expressed in terms of megabytes 100m or gigabytes 2G). Not just the Heap size.

One can consider using the percentage flags Min|MaxRAMPercentage, and InitialRAMPercentage, which give more flexibility with a precise percentage of the heap usage:

  • InitialRAMPercentage=N: Set initial heap size as a percentage of total memory
  • MaxRAMPercentage=N: Set maximum heap size as a percentage of total memory
  • MinRAMPercentage=N: Set minimum heap size as a percentage of total memory

Although being deprecated1, the fraction forms can still be used:

  • InitialRAMFraction: Set the initial heap as a fraction of the the RAM.
  • Using Min|MaxRAMFraction sets the JVM to use available memory/MaxRAMFraction as max heap, and has default as 4, so the Heap occupies 25% of the memory1:

The relation between the above flags above is given by:

MaxHeapSize = MaxRAM * 1 / MaxRAMFraction

These flags are described on the solution Usage of OpenJDK 11 flags InitialRAMPercentage and MaxRAMPercentage and require -XX:+UseContainerSupport flag, which is enabled by default. And If you set a value for Xms/Xmx, these options are ignored.

Other flags

One should not to use UseCGroupMemoryLimitForHeap on JDK 1.8.191 or later. This flag tells the JVM to use the information in /sys/fs/cgroup/memory/memory.limit_in_bytes to calculate memory defaults.

Category

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.