
The Java garbage collector is the single most impactful JVM setting for Minecraft server performance. It determines when the server pauses to clean up unused memory, how long those pauses last, and how smoothly the tick loop runs. Most server operators copy JVM flags from a guide without understanding what they do. This post explains the two main options, when each one wins, and how to choose.
What the Garbage Collector Does
Java manages memory automatically. When your Minecraft server creates objects (entity data, chunk data, block states, player inventories), Java allocates memory for them. When those objects are no longer needed, the garbage collector reclaims that memory.
The problem is timing. When the GC runs, it can pause the server's main thread. In Minecraft, that main thread is the tick loop. A 50ms GC pause means the server misses a tick. Miss enough ticks and your TPS drops below 20, which players experience as rubber-banding, delayed block breaks, and laggy mob behavior.
Different garbage collectors have different strategies for minimizing these pauses.
G1GC: The Established Choice
G1GC (Garbage-First Garbage Collector) has been the default recommendation for Minecraft servers since around 2018. Aikar's famous flags use G1GC, and most hosting providers configure their servers with it.
How G1GC Works
G1GC divides the heap into regions and collects the regions with the most garbage first (hence "Garbage-First"). It does most of its work concurrently (in the background) but still needs short stop-the-world pauses for certain operations.
Aikar's G1GC Flags
The standard G1GC configuration for Minecraft:
java -Xms10G -Xmx10G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -jar server.jar
These flags are tuned for Minecraft's allocation patterns. MaxGCPauseMillis=200 is G1's pause-time goal (200 ms is also the JVM default), and MaxTenuringThreshold=1 promotes surviving objects to the old generation quickly, so long-lived objects such as loaded chunks are not copied back and forth between survivor spaces.
G1GC Strengths
- Battle-tested with Minecraft for years
- Aikar's flags are optimized specifically for Minecraft's memory patterns
- Good throughput (total GC time as percentage of runtime)
- Works well from 4 GB to 16 GB heap sizes
- Available on every Java version Minecraft uses, including Java 21 and Java 25
G1GC Weaknesses
- Young and mixed collections stop the server while they run. G1 marks live objects concurrently, but copying them out of regions happens in stop-the-world pauses (Oracle G1 guide)
- The pause goal is a goal, not a promise. With big heaps and a lot of live data, some pauses run long
- Pause time varies. Most pauses are short, the occasional long one is what players feel
ZGC: The Low-Latency Alternative
ZGC (Z Garbage Collector) was designed for applications that need consistently low pause times, whatever the heap size. Oracle's documentation says ZGC "performs all expensive work concurrently, without stopping the execution of application threads for more than a millisecond", and that pause times are independent of heap size (Oracle ZGC guide).
ZGC Flags for Minecraft
Java 25 (Minecraft 26.1 and newer):
java -Xms12G -Xmx12G -XX:+UseZGC -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -jar server.jar
Java 21 (Minecraft 1.20.5 to 1.21.11):
java -Xms12G -Xmx12G -XX:+UseZGC -XX:+ZGenerational -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -jar server.jar
ZGC needs far fewer tuning flags than G1GC, because it adapts itself: Oracle says it resizes generations, scales its GC threads and adjusts tenuring thresholds on its own. On Java 21, plain -XX:+UseZGC still runs the older non-generational mode, so you add -XX:+ZGenerational (JEP 439). Java 23 made generational ZGC the default (JEP 474) and Java 24 removed the old mode (JEP 490), so on Java 25 the extra flag only prints a warning. Setting -Xms equal to -Xmx together with -XX:+AlwaysPreTouch is what Oracle suggests when low latency is the reason you run ZGC.
ZGC Strengths
- Pauses under a millisecond, independent of heap size
- Large heaps do not make pauses longer
- More consistent tick times when GC pauses were your problem
- Generational ZGC (Java 21 with a flag, default from Java 23) collects short-lived objects more often, which suits Minecraft's allocation pattern
ZGC Weaknesses
- Needs headroom. Because ZGC collects while the server keeps running, the heap must hold your live data plus room for new allocations during a collection. Oracle's advice is simply that "the more memory you give to ZGC the better"
- Some throughput cost: the concurrent GC threads use CPU time the server could otherwise use
- Generational ZGC needs Java 21 or newer
- Fewer Minecraft-specific tuning guides available
How to Compare Them on Your Own Server
Pause times depend on your heap size, how much data stays alive (loaded chunks, entities, plugin caches) and how fast the server allocates. Measuring them takes an evening per collector:
- Run your normal setup on G1GC. At a busy time, run
/spark gcfor the GC history and/spark tpsfor tick times. Start/spark tickmonitor --threshold-tick 50to log every tick that goes over the 50 ms budget; it reports GC activity by default. - Write down the number of players online, the longest GC pause, how often pauses over 50 ms happen, the median and 95th percentile MSPT, and the memory use your panel shows for the whole process.
- Switch to ZGC with a bit more heap if your plan allows, restart, and repeat at a similar time with a similar number of players.
- Compare. If G1 never paused long enough to hurt a tick, ZGC will not give you anything noticeable. If long G1 pauses lined up with your lag spikes and they are gone on ZGC, keep it.
The spark commands are described in the spark command reference.
Which One Should You Use
Use G1GC (Aikar's flags) if:
- Your heap is about 4 to 12 GB
- You are on managed hosting where you pay per GB of RAM
- Your GC pauses do not line up with your lag spikes
- You want the most tested, most documented configuration
Use ZGC if:
- Your heap is 12 GB or larger (heavy modpacks)
- You are on Java 21 or newer, so you get generational ZGC
- Tick consistency matters more to you than raw efficiency
- You have RAM to spare for extra heap headroom
- You run a competitive or PvP server where micro-stutters affect gameplay
The 12 GB line is our own starting point, not a limit set by the JDK. On Java 21, always add -XX:+ZGenerational; on Java 25 generational ZGC is the only mode and the flag is not needed.
How to Switch
From G1GC to ZGC
- Replace your JVM flags. Remove all G1GC-specific flags (
-XX:G1*,-XX:MaxGCPauseMillis,-XX:SurvivorRatio,-XX:+UnlockExperimentalVMOptionsand so on) and use the ZGC line for your Java version from above - Give ZGC more heap than you gave G1 if your plan's RAM allows it, and leave room outside the heap for the JVM itself
- Check your Java version: Java 21 needs
-XX:+ZGenerational, Java 25 does not - Restart and watch
/spark gcand/spark tpsfor the first busy hour
From ZGC to G1GC
- Replace
-XX:+UseZGCwith-XX:+UseG1GCand add Aikar's G1GC flags - You can reduce Xmx slightly since G1GC needs less headroom
- Restart and monitor
In both cases, the switch is just a flag change. No world data or configuration is affected. You can switch back and forth to test which one works better for your specific server.
Monitoring GC Performance
Use Spark (the Minecraft profiling plugin) to monitor GC behavior:
/spark gc
/spark gcmonitor
/spark gc prints the server's GC history and /spark gcmonitor switches live GC monitoring on or off (spark docs). If you regularly see pauses over 50 ms on G1GC, the length of a full tick, ZGC is worth a try. If ZGC is busy collecting all the time, give it more heap or go back to G1GC.
Hosting Considerations
The GC choice interacts with your plan's RAM and CPU. On Space-Node Minecraft hosting, the CPU depends on the location and tier:
- Standard in the Netherlands, Germany, Poland and Canada, and the India location: Ryzen 9 3900X-class with DDR4. On smaller heaps, G1GC with Aikar's flags is the sensible default, since it leaves more CPU time for the server itself.
- Premium in the Netherlands, Germany, Poland and Canada (Ryzen 9 7950X or 9950X depending on stock, DDR5), the USA locations (Ryzen 9 7950X3D) and Singapore and Australia (Ryzen 9 5950X): either collector works. If you run a heavy modpack on 12 GB or more and GC pauses show up in spark, test generational ZGC.
Whatever the plan, measure before and after the switch. The plan selector shows the CPU for each location before you order.
FAQ
Can I use Shenandoah GC instead?
Shenandoah is another low-pause collector, but it gets far less attention in Minecraft guides than ZGC, so there is less shared experience to lean on. It works, and if your JDK ships it you can test it the same way as ZGC: -XX:+UseShenandoahGC, no G1 flags, and measure with spark.
Do Aikar's flags work with ZGC? No. Aikar's flags are G1GC-specific. Many of the parameters (G1NewSizePercent, G1HeapRegionSize, etc.) are meaningless to ZGC and may cause errors. Use ZGC-specific flags when switching.
Should I use GraalVM with ZGC? GraalVM's JIT compiler and ZGC are independent features. You can run ZGC on GraalVM if you want both the JIT improvements and low GC pauses. Check our GraalVM Minecraft guide for setup details.
Related guides: for the full JVM flags reference, see Aikar's flags guide. For another performance angle, read GraalVM for Minecraft servers. Running a modpack? See ATM9 vs ATM10 comparison and best Minecraft mods 2026.
Does the GC choice affect chunk loading speed? Indirectly. A long G1 pause stops everything on the server, including the tick that hands out loaded chunks. ZGC's short pauses remove that particular delay, but chunk generation itself is CPU work that no collector speeds up. Pre-generating your world helps far more.
What about client-side GC settings? The client benefits from ZGC too, especially with shaders and high render distances. But the server is where GC configuration matters most, because the server tick loop is the bottleneck for everyone's gameplay experience.