javadockerkubernetesmemoryheap-memory

How to make JVM only use large heap size when necessary


I have a Java application running in a container (eclipse-temurin:21-jre-alpine) in a Kubernetes cluster, and I'm trying to optimize its memory usage.

The required heap size is very spiky. By default, unless very large requests are received, almost no memory is necessary. However, occasionally, large heap is necessary.

I'm struggling with making the JVM use as little RAM as possible under normal conditions, but still allow for occasional spikes in memory usage without crashing the container.

I set a Kubernetes resource limit of 1GB for the container, which the JVM picked up. After configuring this limit, the RAM usage dropped from 4GB to 400MB, with no noticeable reduction in service quality.

However, occasionally, the application needs to load up to, say 3GB into memory for certain requests, causing the container to now crash due to the 1GB hard limit.

How can I configure the JVM and Kubernetes to set a hard limit (e.g., 5GB) but ensure the JVM uses minimal RAM under normal conditions?


Solution

  • Inspired by this SO answer, I set the following JVM args and that resolved the issue mostly:

    -XX:+UseG1GC -XX:MaxHeapFreeRatio=5 -XX:MinHeapFreeRatio=2
    

    Now I can have a high memory limit like 5GB in case that much is required for spikes, but the usual RAM usage will still be less than 1GB.

    Apparently the JVM really likes to hold on to memory, and even if something is garbage collected, it's not released to the OS just in case it might be required in the future.