In a container, MaxRAMPercentage is calculated basing on cgroup memory.limit_in_bytes value.
It is limits Kubernetes configuration that affects cgroup memory limit. So, in your case, the maximum heap size will be set to 256M (50% of the limit).
What happens when heap size set by XX:MaxRAMpercentage is exceeded?
Should we set -Xmx (max java heap size) in a kubernetes container - Stack Overflow
java - -XX:MaxRAMPercentage with respect to K8S request and limit - Stack Overflow
May Kubernetes restart java container if java heap size exceeds allowed from -XX:MaxRAMPercentage - Stack Overflow
Since Java 10 and Java 8u191 there should be quite good support for Java determining actual memory available in the container. It's not necessary to use -Xmx anymore. It's recommended to use XX:MaxRAMPercentage with a value ranging between 75.0 and 80.0 (e.g. XX:MaxRAMPercentage=75.0)
Sources:
https://www.atamanroman.dev/development/2019/09/11/usecontainersupport-to-the-rescue.html
https://pretius.com/blog/jvm-kubernetes/
If you don't set a maximum heap size the behavior depends on the java version used. Current JREs support determining the container limits and use that to guide their internal heuristics.
when running with an older version the memory to be used for the heap will be determined based on the physical memory visible to the container, which might be excessive.
Note that hitting the memory limit of the container will lead to killing the process and restarting the container.
I suggest you use a sane value, as determined from operational testing, for Xmx and Xms to get a stable memory usage.
The default "max heap" if you don't specify -Xmx is 1/4 (25%) of the host RAM.
JDK 10 improved support for containers in that it uses container's RAM limits instead of underlying host. As pointed by @David Maze this has been backported to JDK 8.
Assuming you have a sufficiently recent version of JDK 8, you can use -XX:MaxRAMPercentage to modify the default percentage of total RAM used for Max heap. So instead of specifying -Xmx you can tell, e.g. -XX:MaxRAMPercentage=75.0. See also https://blog.arkey.fr/2020/10/27/maxrampercentage-is-not-what-i-wished-for/
Here's an example using alpine JDK docker image: https://hub.docker.com/_/openjdk (see section "Make JVM respect CPU and RAM limits" in particular).
# this is running on the host with 2 GB RAM
docker run --mount type=bind,source="$(pwd)",target=/pwd -it openjdk:8
# running with MaxRAMPercentage=50 => half of the available RAM is used as "max heap"
root@c9b0b4d9e85b:/# java -XX:+PrintFlagsFinal -XX:MaxRAMPercentage=50.0 -version | grep -i maxheap
uintx MaxHeapFreeRatio = 100 {manageable}
uintx MaxHeapSize := 1044381696 {product}
openjdk version "1.8.0_265"
OpenJDK Runtime Environment (build 1.8.0_265-b01)
OpenJDK 64-Bit Server VM (build 25.265-b01, mixed mode)
# running without MaxRAMPercentage => default 25% of RAM is used
root@c9b0b4d9e85b:/# java -XX:+PrintFlagsFinal -version | grep -i maxheap
uintx MaxHeapFreeRatio = 100 {manageable}
uintx MaxHeapSize := 522190848 {product}
openjdk version "1.8.0_265"
In my K8s setup, I am using consul to manage the pod configuration. Here is a command to override the jvm setting on the fly. It is a pretty much project specific but it might give you a hint if you are using consul for configuration.
kubectl -n <namespace> exec -it consul-server -- bash -c "export CONSUL_HTTP_ADDR=https://localhost:8500 && /opt/../home/bin/bootstrap-config --token-file /opt/../config/etc/SecurityCertificateFramework/tokens/consul/default/management.token kv write config/processFlow/jvm/java_option_xmx -Xmx8192m"
Hey r/java,
I work in performance optimization within a large enterprise environment. Our stack is primarily Java-based IS running in Kubernetes clusters. We're talking about a significant scale here – monitoring and tuning over 1000 distinct Java applications/services.
A common configuration standard in our company is setting -XX:MaxRAMPercentage=75.0 for our Java pods in Kubernetes. While this aims to give applications ample headroom, we've observed what many of you probably have: the JVM can be quite "greedy." Give it a large heap limit, and it often appears to grow its usage to fill a substantial portion of that, even if the application's actual working set might be smaller.
This leads to a frequent challenge: we see applications consistently consuming large amounts of memory (e.g., requesting/using >10GB heap), often hovering near their limits. The big question is whether this high usage reflects a genuine need by the application logic (large caches, high throughput processing, etc.) or if it's primarily the JVM/GC holding onto memory opportunistically because the limit allows it.
We've definitely had cases where we experimentally reduced the Kubernetes memory request/limit (and thus the effective Max Heap Size) significantly – say, from 10GB down to 5GB – and observed no negative impact on application performance or stability. This suggests potential "greed" rather than need in those instances. Successfully rightsizing memory across our estate would lead to significant cost savings and better resource utilization in our clusters.
I have access to a wealth of metrics :
-
Heap usage broken down by generation (Eden, Survivor spaces, Old Gen)
-
Off-heap memory usage (Direct Buffers, Mapped Buffers)
-
Metaspace usage
-
GC counts and total time spent in GC (for both Young and Old collections)
-
GC pause durations (P95, Max, etc.)
-
Thread counts, CPU usage, etc.
My core question is: Using these detailed JVM metrics, how can I confidently determine if an application's high memory footprint is genuinely required versus just opportunistic usage encouraged by a high MaxRAMPercentage?
Thanks in advance for any insights!