Container Resource Limits for JVM Apps: CPU and Memory Configuration
Fast recall
Quick revision
Core answer
Understanding of how resource limits work in containerized JVM environments
Not setting resource limits at all (allowing one pod to starve others on the node) or setting limits too tight (JVM can't start metaspace or code cache)
Explain QoS classes (Guaranteed, Burstable, BestEffort), discuss CPU throttling vs OOM, and show how the JVM reads CPU quota via /sys/fs/cgroup/cpu
Interview-ready guide
Interview answer
Docker resource limits (CPU and memory) are enforced by the Linux kernel's cgroup mechanism. The JVM sees these limits through the cgroup filesystem — if it fails to read them correctly, it sizes its heap for the host machine rather than the container.
Memory limits — heap sizing
- Set the Docker memory limit with
--memory=512mor in Compose withmem_limit: 512m; the JVM reads this limit via cgroup v1 or v2 and adjusts heap accordingly with-XX:+UseContainerSupport(enabled by default since JDK 10) -XX:MaxRAMPercentage=75.0allocates 75% of the container memory limit to heap — the remaining 25% covers Metaspace, thread stacks, direct buffers, and the JVM overhead; setting this above 85% causes OOMKill because the total process exceeds the limit before the GC has time to respond- Always set
--memoryand explicit JVM flags together; relying solely onUseContainerSupportwithoutMaxRAMPercentageleaves heap at the default 25% of container RAM — a 512 MB container gets a 128 MB heap, which is too small for most Spring Boot applications
CPU limits — thread pool sizing
--cpus=0.5gives the container half of one CPU core;--cpus=2gives it two cores — the JVM reads the CPU quota viaRuntime.getRuntime().availableProcessors()and uses this for default thread pool sizes- Spring Boot's Tomcat thread pool and ForkJoinPool both use
availableProcessors()as a baseline — in a container with--cpus=0.5, the JVM sees 1 available processor, and default thread pool sizing becomes far too small for a production workload - Set explicit thread pool sizes with
server.tomcat.threads.max=50rather than relying on the JVM's available processor count — the correct pool size depends on the workload's I/O blocking factor, not the CPU quota
Resource requests vs limits in Kubernetes
- Docker
--memorymaps to Kubernetesresources.limits.memory; always also setresources.requests.memory— requests determine scheduling placement, limits determine cgroup enforcement - A pod with only limits and no requests gets scheduled as if it has no resource requirements, potentially landing on an already-saturated node
- Set requests equal to limits for a Guaranteed QoS class — the kubelet guarantees the pod will not be evicted under memory pressure; without this, a Burstable pod gets evicted before other workloads under node memory pressure
Container Resource Limits for JVM Apps: CPU and Memory Configuration
Direct Answer
Set memory limits in Kubernetes (resources.limits.memory) and use -XX:MaxRAMPercentage=75.0 so the JVM respects cgroup limits. For CPU, set resources.limits.cpu and the JVM automatically adjusts thread pool sizing via -XX:+UseContainerSupport. Set requests smaller than limits for better bin-packing; set Guaranteed QoS (requests=limits) for latency-sensitive services.
Common Mistakes & Pitfalls
Not setting resource limits at all (allowing one pod to starve others on the node) or setting limits too tight (JVM can't start metaspace or code cache)
Check your understanding
Answer these without looking back at the article.
- What is CPU throttling in cgroups and how does it affect JVM GC pauses?
- How do Kubernetes QoS classes affect pod eviction order during node pressure?
- How would you diagnose that a pod is being CPU-throttled rather than memory-constrained?
Related interview prep categories
2Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.