The JVM inside a container

JVM · lesson 25 of 34 · 3 min read

Modern JVMs read the cgroup limits, so the defaults are sane - until someone hardcodes -Xmx.

Open this lesson in the learning hub

Key points

  • Since Java 10 the VM reads the cgroup memory and cpu limits, not the whole machine. UseContainerSupport is on by default.
  • With no flags the heap is a quarter of the container limit, which leaves three quarters unused. That is usually far too cautious.
  • Prefer -XX:MaxRAMPercentage=75 to a fixed -Xmx. It follows the limit when somebody edits the deployment.
  • availableProcessors() follows the cpu quota, and that number sizes the common pool, the GC threads and most client libraries.
  • A cpu limit of 0.5 rounds up to one processor, so the VM picks the Serial collector and single-threaded defaults all round.
  • Check what it really saw: java -XX:+PrintFlagsFinal -version run inside the image, not on your laptop.

Example

import com.sun.management.HotSpotDiagnosticMXBean;
import com.sun.management.VMOption;
import java.lang.management.ManagementFactory;

public class Main {

    static final String[] FLAGS = {
            "UseContainerSupport", "MaxRAMPercentage", "MaxHeapSize", "UseG1GC", "UseSerialGC"
    };

    public static void main(String[] args) {
        Runtime rt = Runtime.getRuntime();
        System.out.println("what this JVM believes it has been given:");
        System.out.println("  availableProcessors : " + rt.availableProcessors()
                + "   (follows the cpu quota, and sizes the common pool and the GC)");
        System.out.println("  max heap            : " + (rt.maxMemory() >> 20) + " MB");
        System.out.println();

        HotSpotDiagnosticMXBean diag =
                ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
        System.out.println("and where each of those numbers came from:");
        for (String flag : FLAGS) {
            try {
                VMOption opt = diag.getVMOption(flag);
                System.out.printf("  %-20s = %-14s origin: %s%n",
                        flag, opt.getValue(), opt.getOrigin());
            } catch (IllegalArgumentException e) {
                System.out.printf("  %-20s   not present - this one only exists on Linux%n", flag);
            }
        }
        System.out.println();
        System.out.println("origin DEFAULT means nobody set it and the VM worked it out itself.");
        System.out.println("Inside a 1 GB container with no flags that is a 256 MB heap: a");
        System.out.println("quarter of the limit, and three quarters of the memory unused.");
    }
}

Set MaxRAMPercentage, not -Xmx, and let the JVM read the limit.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the JVM course, and every lesson in it is listed on the JVM contents page.