Minecraft JVM Arguments That Actually Cut GC Stutter: Aikar’s Flags vs G1GC vs ZGC

Verified on Minecraft 26.1 (Java 25 bundled client runtime) and Paper/Spigot servers on Java 21–25. Flag behavior can shift between major Java releases — re-check before copying an old guide’s flags onto a new server.

Quick Start

  1. Decide client or server — the right garbage collector isn’t the same for both (see below).
  2. Check your allocated RAM. Under 12 GB, default to G1GC with Aikar’s flags. At 12 GB or more, ZGC is worth testing.
  3. Copy the matching block from the RAM-tier section and paste it into your launcher’s JVM arguments field or server start script.
  4. Set -Xms equal to -Xmx. A mismatched pair forces the JVM to resize the heap mid-game, which causes its own stutter.
  5. Delete any flag you don’t recognize from an old guide before adding new ones — see “What to Skip” for the ones that actively hurt you.

Why GC Pauses Break Minecraft’s Tick Loop

Minecraft runs on a fixed 20-tick-per-second loop — one tick every 50 milliseconds [5]. Every entity update, redstone calculation, and chunk load has to fit inside that window, and so does garbage collection. When the JVM needs to reclaim memory, it briefly stops every other thread, including the tick loop. If that pause runs longer than the remaining budget in the current tick, the tick overruns, MSPT climbs above 50, and TPS drops below 20 [5]. Untuned default flags don’t manage this well — they let the heap fill up before collecting, which produces long, infrequent pauses instead of short, frequent ones. Tuning the collector doesn’t reduce the total GC work; it reshapes it into pieces small enough to fit inside your tick budget. If you’ve ever watched a server freeze for a full second the moment a village of villagers all path-find at once, that’s the symptom — a GC pause colliding with a spike in allocation, not the pathfinding itself being slow.

Before and after frametime comparison graph showing reduced garbage collection stutter
Before-and-after frametime graph showing GC pause reduction with tuned flags.

Aikar’s Flags, Explained Instead of Just Copied

Aikar’s flags are a G1GC tuning preset written by a former PaperMC developer specifically for Minecraft’s memory pattern — lots of small, short-lived objects (entities, packets, block updates) mixed with a smaller set of long-lived ones (loaded chunks, player data). PaperMC’s own docs still list them as the baseline JVM config for most servers [1]:

-Xms8G -Xmx8G -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

Three of these flags are worth understanding rather than just pasting:

FlagWhat it actually does
G1NewSizePercent=30Reserves 30% of the heap for the young generation by default, where G1 normally only guarantees 5% [3]. Since most Minecraft objects die young, a bigger young generation means more garbage gets collected in cheap young-only pauses instead of expensive mixed collections.
InitiatingHeapOccupancyPercent=15Starts concurrent marking once the old generation is 15% full, well below G1’s 45% default [3]. Marking earlier gives G1 more time to finish before the heap actually fills, which avoids the fallback full GC that freezes the whole server for seconds.
MaxGCPauseMillis=200A target, not a guarantee — Oracle’s own docs describe G1 as trying to hit this “with high probability… but not always with absolute certainty” [3]. It tells G1 how aggressively to shrink the young generation to stay under budget, not a hard ceiling on any single pause.

Set -Xms/-Xmx to your actual allocation and PaperMC’s own guidance still holds: budget at least 6–10 GB for a Paper server regardless of player count, and if the host machine only has 8 GB total, cut 1–1.5 GB off the top for the OS rather than allocating all of it [1].

G1GC vs ZGC: The Decision Nobody States Clearly

Generational ZGC became a real option once Java 21 shipped, and it changes the pause-time math completely — Oracle documents concurrent collection that stalls application threads for under a millisecond, independent of heap size [2]. That sounds like a strict upgrade over G1’s 200ms target. It isn’t, and the reason most guides skip is that G1GC and ZGC don’t fail the same way on the same hardware.

Community benchmarking that specifically tested Minecraft (not a generic Java workload) found ZGC produced no measurable server throughput hit, but caused a noticeable client FPS hit on an 8-core/16-thread laptop running the same flags [9]. Shenandoah, ZGC’s closest concurrent-collector sibling, showed the opposite pattern — solid on the client, but it “kills server throughput” under load [9]. Treating “best GC” as one answer for both a singleplayer world and a dedicated server is exactly the gap that produces bad copy-paste advice.

SituationRecommended GCWhy
Client, singleplayer or LANG1GC (default)ZGC’s concurrent-collection overhead has shown up as an FPS cost on real hardware, for a pause-time win the client mostly doesn’t need [9].
Server, under 12 GB heapG1GC + Aikar’s flagsThis is the production-tested combination; ZGC’s memory overhead eats into small heaps faster than it helps [8].
Server, 12 GB+ heapZGC (generational)Sub-millisecond pauses matter more once a single G1 mixed collection has enough live data to take noticeably longer, and ZGC’s overhead is proportionally smaller on a large heap [2][8].
Modded server prone to long single-object allocations (heavy modpacks)ZGC, test firstConcurrent collection avoids G1’s fallback full-GC stall, but always benchmark your specific modpack before committing — results vary by allocation pattern [9].

One freshness note worth flagging: Minecraft 26.1 moved the bundled client runtime to Java 25 [6][7], while many Paper/Spigot servers still run Java 21 because that’s what 1.21.x requires [7]. If you’re copying flags between your client and your own server, don’t assume they’re running the same Java version — check both before assuming a flag behaves identically.

Copy-Paste Blocks by RAM Tier

Three JVM flag configuration panels representing different RAM allocation tiers
Copy-paste flag block panels for 4 GB, 8 GB, and 12 GB allocations.

Xms and Xmx are matched in each block on purpose — an unmatched pair lets the JVM resize the heap live, which is its own stutter source.

4 GB allocation (client, budget server):

-Xms4G -Xmx4G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=4M -XX:G1ReservePercent=20 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCCountTarget=4 -XX:SurvivorRatio=32

8 GB allocation (typical small-to-mid survival server):

-Xms8G -Xmx8G -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:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1

12 GB allocation (large modpack or high player count — ZGC territory) [2][8]:

-Xms12G -Xmx12G -XX:+UseZGC -XX:+ZGenerational -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UnlockExperimentalVMOptions

Notice the 12 GB block is shorter — Oracle’s own guidance is that ZGC is designed to need minimal manual tuning and adapts generation sizes and thread counts on its own [2]. Bolting G1-specific flags onto a ZGC command line does nothing; they’re silently ignored because they belong to a different collector.

What to Skip: Flags That Crash or Do Nothing on Modern Java

Minecraft’s own wiki still shows a legacy startup script using -XX:+UseConcMarkSweepGC, -XX:+CMSIncrementalPacing, and -XX:+AggressiveOpts as an example [4] — and old forum threads keep it circulating. All three are dead weight or worse on anything running Java 14+:

  • -XX:+UseConcMarkSweepGC and related CMS flags — the CMS collector was deprecated in Java 9 and fully removed in Java 14 [11]. On modern Java this doesn’t just underperform, it refuses to start: the JVM won’t recognize the flag.
  • -XX:+AggressiveOpts — deprecated in Java 11 with a warning, silently ignored in Java 12, then a hard failure from Java 13 onward: the JVM won’t even start, reporting “Unrecognized VM option ‘AggressiveOpts’” [12]. On Java 21 or 25, this one flag alone is enough to stop your server from launching.
  • -XX:PermSize / -XX:MaxPermSize — leftovers from Java 7 and earlier, before permanent generation was replaced by Metaspace. Harmless but pointless on any Java version you’d actually run Minecraft on today.
  • -XX:G1ConcRSHotCardLimit and -XX:G1ConcRefinementServiceIntervalMillis — removed in Java 21.0 and 20.0 respectively [10]. Unlike CMS, these fail silently rather than crashing the server, which makes them easy to carry forward for years without noticing they do nothing.

If a flag you’re copying from an old guide isn’t in Aikar’s current list, PaperMC’s docs, or Oracle’s own tuning guide, treat it as a candidate for removal rather than an extra safety margin.

Which Setup Are You Actually Running?

SetupPriority
New to JVM tuning, just want fewer client stuttersUse the matching RAM-tier G1GC block as-is. Don’t touch individual flag values until you understand why one exists.
Running a small self-hosted survival server (2–10 players)Aikar’s flags at your actual RAM, no further tuning needed — this combination is already production-tested [1].
Hosting a large modpack or 50+ player serverBenchmark ZGC against your current G1GC setup before switching — the throughput/pause-time trade-off depends on your specific allocation pattern [9].
Want to verify pause times yourself instead of trusting a guideAdd GC logging (-Xlog:gc*:logs/gc.log:time,uptime:filecount=5,filesize=1M on Java 11+) and read your own server’s actual pause distribution [1].

FAQ

Do Aikar’s flags still matter on Java 21 and 25?

Yes for the G1-specific tuning values — PaperMC’s docs still list the same flag set as current guidance [1] — but the GC logging syntax changed at Java 11, and a handful of older G1 sub-flags from pre-Java-20 guides have since been removed outright [10]. The core preset is stable; the peripheral flags around it aren’t.

Should I just switch everything to ZGC since it has lower pause times?

No — lower pause times on paper don’t mean better performance for your setup. The only Minecraft-specific benchmark data available shows ZGC helping server throughput while costing client FPS on identical hardware [9]. If you’re tuning a client, that trade-off usually isn’t worth it.

Why do my old JVM flags suddenly not work after updating Java?

Most likely you’re carrying CMS-era flags forward. They were deprecated in Java 9 and removed in Java 14 [11], so a server that ran fine on Java 8 will refuse to start with the exact same flags on Java 17 or later. Swap to a current G1GC or ZGC block rather than debugging the old one — chasing a “why won’t my server start” error back to a nine-year-old forum post’s flag string is a common enough time-sink that it’s worth checking your flags against a current source first.

For the launcher-side settings that pair with these flags, see our Best Minecraft Java Edition FPS Settings guide, and if you’re specifically fighting slow Forge startup times, How to Make Minecraft Forge Load Faster covers the loading-specific flags this guide doesn’t. Both are part of our Minecraft Complete Guide hub.

Sources

  1. Aikar’s Flags — PaperMC Docs
  2. The Z Garbage Collector — Oracle Java SE GC Tuning Guide
  3. The Garbage-First Garbage Collector — Oracle Java SE GC Tuning Guide
  4. Tutorial: Server Startup Script — Minecraft Wiki
  5. Tick — Minecraft Wiki
  6. Tutorial: Update Java — Minecraft Wiki
  7. Minecraft Java Version Requirements — ModReady.gg
  8. JVM Arguments — CleanroomMC Wiki
  9. Minecraft Performance Flags Benchmarks — brucethemoose (GitHub)
  10. Java 21 Deprecations Issue #53 — brucethemoose (GitHub)
  11. JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector — OpenJDK
  12. JDK 13: What AggressiveOpts? — Inside Java Blogging
Michael R.
Michael R.

I've been playing video games for over 20 years, spanning everything from early PC titles to modern open-world games. I started Switchblade Gaming to publish the kind of accurate, well-researched guides I always wanted to find — built on primary sources, tested in-game, and kept up to date after patches. I currently focus on Minecraft and Pokémon GO.