After JDK-8253413, GC could unnecessarily trigger full GCs even when heap utilization is low. I discovered this case with the test hotspot/jtreg/gc/class_unloading/TestG1ClassUnloadingHWM.java.
To reproduce, add "-XX:G1HeapRegionSize=32m" to the run() function of TestG1ClassUnloadingHWM.java for launching TestG1ClassUnloadingHWM.AllocateBeyondMetaspaceSize. Optionally, also set a fixed heap size such as -Xms512m -Xmx512m to make the problem more obvious. Then the testWithG1ClassUnloading() case would fail, with GC log looks like:
[0.006s][info][gc] Using G1
[0.065s][info][gc] GC(0) Pause Young (Concurrent Start) (Metadata GC Threshold) 34M->33M(512M) 4.710ms
[0.065s][info][gc] GC(1) Concurrent Mark Cycle
[0.069s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 33M->32M(512M) 1.386ms
[0.079s][info][gc] GC(3) Pause Full (G1 Compaction Pause) 32M->32M(512M) 9.201ms
[0.079s][info][gc] GC(1) Concurrent Mark Cycle 13.463ms
[0.088s][info][gc] GC(4) Pause Young (Concurrent Start) (Metadata GC Threshold) 32M->32M(512M) 0.891ms
[0.088s][info][gc] GC(5) Concurrent Mark Cycle
[0.091s][info][gc] GC(6) Pause Young (Normal) (G1 Evacuation Pause) 32M->32M(512M) 0.621ms
[0.097s][info][gc] GC(7) Pause Full (G1 Compaction Pause) 32M->32M(512M) 5.227ms
[0.097s][info][gc] GC(5) Concurrent Mark Cycle 8.723ms
[0.108s][info][gc] GC(8) Pause Young (Normal) (G1 Evacuation Pause) 64M->32M(512M) 0.668ms
[0.113s][info][gc] GC(9) Pause Full (G1 Compaction Pause) 32M->32M(512M) 5.266ms
[0.120s][info][gc] GC(10) Pause Young (Normal) (G1 Evacuation Pause) 64M->32M(512M) 0.665ms
[0.124s][info][gc] GC(11) Pause Full (G1 Compaction Pause) 32M->32M(512M) 4.376ms