We are reviewing our next newsletter, which is all about Kubernetes and I thought that I would share an article about Java running on Kubernetes here first.
The 2026 State of Java on Kubernetes report by Akamas highlights a growing “performance tax” being paid by teams relying on container defaults. Despite Java 17/21 modernization, the data shows that 60% of JVMs still run with an unset Garbage Collector, leading to major efficiency gaps.
The 25% Heap Paradox: By default, the JVM often only allocates ~25% of container memory to the heap. If you request 2GB but don’t set MaxRAMPercentage, you’re effectively paying for 1.5GB of overhead that your app can’t even touch, leading to premature GC thrashing.
CPU Throttling & Micro-bursting: Standard CFS quotas often penalize the JVM during JIT compilation or concurrent GC phases. The report found that larger replicas (e.g., 2 replicas with 3 CPUs) significantly outperform small, fragmented ones (6 replicas with 1 CPU) due to reduced context switching and better “breathing room” for background GC threads.
The G1GC Collision: On smaller nodes (exactly 2 CPUs), G1GC’s parallel worker threads can actually crowd out your application logic, turning a “modern” collector into a bottleneck due to core contention.
The Takeaway: If you aren’t explicitly aligning your MaxRAMPercentage and tuning ParallelGCThreads to your actual K8s limits, your “cloud-native” Java is not giving you the performance you expect.
Anyone (@Erik ?) have an interesting story about playing with the JVM defaults? Good/Bad?