Why Does G1’s “to-space exhausted” Spike Memory Usage? Causes & Tuning Tips
The article explains a sudden 90% memory jump after upgrading to G1, analyzes the “to-space exhausted” GC log, clarifies Xmx/Xms behavior, and provides concrete JVM tuning recommendations to prevent promotion failures and long pauses.
Recently I upgraded an application from a CMS to the G1 garbage collector. One morning I received an alarm, checked the monitoring, and saw the machine’s memory usage jump about 90% (see image).
Examining the GC logs around that time revealed a "to-space exhausted" entry. The JVM team explained that the old generation ran out of space, forcing all objects in the young generation to be promoted to the old generation, which caused the overall memory consumption to surge.
I also asked why, when Xmx and Xms are set to the same value, the process still appears to allocate more than the configured heap. The JVM team answered that physical memory is only allocated from the OS when the heap is first read or written.
Initial tuning suggestions:
Reduce Xmx and Xms back to their original values to avoid unnecessary full GC.
Add the -XX:HeapDumpAfterFullGC flag so that a heap dump is generated automatically when a similar incident occurs, allowing detailed analysis of the objects consuming memory.
According to "Java Performance: The Definitive Guide" (page 123), this situation is a promotion‑failure case: G1 completes the marking phase, starts a mixed collection, but the old‑generation region is exhausted before enough space is reclaimed.
Two main optimization directions are recommended:
Trigger mixed GC earlier by lowering -XX:InitiatingHeapOccupancyPercent=N (default 45%). Setting it too low can cause excessive concurrent and mixed collections, so a balanced value is needed.
Increase the number of old‑generation regions collected per mixed GC by adjusting -XX:G1MixedGCCountTarget=N (default 8).
It is also important to monitor the "to-space exhausted" and "Evacuation Failure" log entries (see image). The evacuation failure in the example consumed about 684 ms, causing nearly a one‑second pause.
Additional recommendations:
Lower -XX:InitiatingHeapOccupancyPercent to reduce the cost of evacuation failures.
Increase -XX:ConcGCThreads to allocate more threads for garbage collection, keeping in mind the additional CPU overhead.
If survivor space is insufficient for newly promoted objects, consider raising -XX:G1ReservePercent (default 10%).
Finally, always enable -XX:+HeapDumpAfterFullGC and -XX:+HeapDumpOnOutOfMemoryError to capture heap snapshots during full GC or OOM events, and configure GC logging to a dedicated file for easier analysis.
Remember that JVM tuning is not a silver bullet; when OOM or long pauses occur, the application code itself often contains inefficiencies that must be addressed.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
