The 30 second answer
I turn on GC logging and look at how often pauses happen, how long they are, whether they're young or full collections, and how much heap is still used after each one. Frequent young pauses usually mean a high allocation rate. Old-generation usage that keeps climbing after every collection suggests a leak. Full collections in G1 usually mean the heap is too small. Then I reduce allocation, resize, or consider ZGC.
1. Get data:
-Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=5,filesize=20m2. Read the pattern:
| Symptom | Likely cause | Action |
|---|---|---|
| Very frequent young pauses | high allocation rate or small young gen | reduce allocation (JFR allocation profiling), give more heap |
| Long young pauses | many surviving objects to copy | fewer long-lived temporary objects, check promotion |
| Old gen after GC keeps rising | memory leak | heap dump and class histogram |
| G1 full GCs or evacuation failures | heap too small, or marking starts too late | more heap, lower -XX:InitiatingHeapOccupancyPercent |
| Humongous allocations in G1 | objects of at least half a region | larger -XX:G1HeapRegionSize, or avoid huge arrays |
3. Tune carefully: set -Xms equal to -Xmx for steady services, adjust -XX:MaxGCPauseMillis, and avoid fixing the young-generation size with G1. If pause time is the main problem on a large heap, test ZGC.
[Opinion] Most GC problems are solved by allocating less, not by tuning flags.
Sources: GC Tuning Guide: G1 tuning, java command: -Xlog, Troubleshooting Guide