Appendix F. Tuning JVMs to Run in Linux Containers

F.1. Overview

Java processes running inside the Linux container do not behave as expected when you allow JVM ergonomics to set the default values for the garbage collector, heap size, and runtime compiler. When you execute a Java application without any tuning parameters — for example, java -jar mypplication-fat.jar — the JVM automatically sets several parameters based on the host limits, not the container limits.

This section provides information about the packaging of Java applications inside a Linux container so that the container’s limits are taken into consideration for calculating default values.

F.2. Tuning the JVM

The current generation of Java JVMs are not container-aware, so they allocate resources based on the size of the physical host, not on the size of the container. For example, a JVM normally sets the maximum heap size to be 1/4 of the physical memory on a host. On a large host machine, this value can easily exceed the memory limit defined for a container and, if the container limit is exceeded at run time, OpenShift will kill the application.

To solve this issue, you can use the Fuse on OpenShift base image that is capable of understanding that a Java JVM runs inside a restricted container and automatically adjusts the maximum heap size, if not done manually. It provides a solution of setting the maximum memory limit and the core limit on the JVM that runs your application. For Fuse on OpenShift images, it can:

  • Set CICompilerCount based on the container cores
  • Disable C2 JIT compiler when container memory limit is below 300MB
  • Use one-fourth of the container memory limit for the default heap size when below 300MB

F.3. Default Behaviour of Fuse On OpenShift Images

In Fuse on OpenShift, the base image for an application build can either be a Java image (for Spring Boot applications) or a Karaf image (for Karaf applications). Fuse on OpenShift images execute a script that reads the container limits and uses these limits as the basis for allocating resources. By default, the script allocates the following resources to the JVM:

  • 50% of the container memory limit,
  • 50% of the container core limit.

There are some exceptions to this. For Karaf and Java images, when the physical memory is below 300MB threshold, heap size is restored to one-fourth default heap size instead of the one-half.

F.4. Custom Tuning of Fuse on OpenShift images

The script sets the CONTAINER_MAX_MEMORY and CONTAINER_CORE_LIMIT environment variables, which can be read by a custom application to tune its internal resources. Additionally, you can specify the following runtime environment variables that enable you to customize the settings on the JVM that runs your application:

  • JAVA_OPTIONS
  • JAVA_MAX_MEM_RATIO

To customize the limits explicitly, you can set the JAVA_MAX_MEM_RATIO environment variable by editing the deployment.yml file, in your Maven project. For example:

spec:
  template:
    spec:
      containers:
        -
          resources:
            requests:
              cpu: "0.2"
               memory: 256Mi
            limits:
              cpu: "1.0"
               memory: 256Mi
          env:
          - name: JAVA_MAX_MEM_RATIO
            value: 60

F.5. Tuning Third-Party Libraries

Red Hat recommends you to customize limits for any third-party Java libraries such as Jetty. These libraries would use the given default limits, if you fail to customize limits manually.

The startup script exposes some environment variables describing container limits which can be used by applications:

CONTAINER_CORE_LIMIT
A calculated core limit
CONTAINER_MAX_MEMORY
Memory limit given to the container