The JVM reclaims memory automatically, but the collector you choose trades throughput against pause time. Understanding that trade-off is the first step before touching a single flag.
Pick the right collector
G1 is the default and balances pauses with throughput. For large heaps that need very low pauses, ZGC is worth a look.
# G1 with a pause-time goal
java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xmx4g -jar app.jar
# ZGC for very large heaps and sub-millisecond pauses
java -XX:+UseZGC -Xmx16g -jar app.jarRead the logs before tuning
Enable unified GC logging and measure — never tune blind.
java -Xlog:gc*:file=gc.log:time,uptime -jar app.jarTakeaways
- Set
-Xmsequal to-Xmxin containers to avoid heap resizing pauses. - Measure allocation rate before assuming the collector is the problem.
- Most "GC problems" are actually memory leaks or oversized caches.
Reducing allocation — reusing buffers, avoiding needless boxing — usually beats any collector flag you can set.