Java "GC overhead limit exceeded" in CI - Fix Thrashing GC
By Daniel Zoghalchali·Latchkey
The JVM gave up because it was spending more than 98% of its time in garbage collection and recovering less than 2% of the heap. It is a near-OOM: technically there is a sliver of heap left, but GC is thrashing.
What this error means
A long-running test or build slows to a crawl and then fails with java.lang.OutOfMemoryError: GC overhead limit exceeded. CPU is pegged on GC threads just before the failure.
JVM output
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.base/java.util.HashMap.resize(HashMap.java:704)
Common causes
Heap too small for the live set
When the live data nearly fills the heap, GC runs constantly but frees almost nothing. The overhead guard trips before a hard heap-space OOM.
Memory leak slowly filling the heap
A growing retained set (caches, accumulating collections) pushes the heap toward full, and GC overhead climbs until the limit is exceeded.
How to fix it
Increase the heap
A larger -Xmx gives GC room to reclaim, ending the thrashing - assuming there is no leak.
Terminal
java -Xmx4g -jar app.jar
mvn -B test -DargLine="-Xmx2g"
Find and fix the retention
If raising heap only delays the failure, capture a heap dump and locate what is being retained.
Terminal
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./oom.hprof -jar app.jar
# analyze oom.hprof with Eclipse MAT or VisualVM
How to prevent it
Size -Xmx so the live set leaves comfortable headroom for GC.
Profile for leaks when GC overhead climbs over time.
Avoid unbounded in-memory accumulation in long-running steps.
Frequently asked questions
What causes ""GC overhead limit exceeded""?
When the live data nearly fills the heap, GC runs constantly but frees almost nothing. The overhead guard trips before a hard heap-space OOM.
How do I fix "GC overhead limit exceeded"?
A larger -Xmx gives GC room to reclaim, ending the thrashing - assuming there is no leak.
Can Latchkey fix this automatically?
Yes. Latchkey runs your GitHub Actions on managed runners that detect this failure, apply the fix, and retry the job automatically - self-healing is on by default.