Evidence ledger
How we measure
GameServerBench Method

Guide

Java flags for modded Minecraft servers: G1 vs ZGC and heap sizing

For a modded Minecraft server, Paper's documentation recommends Aikar's G1 flag set, and generational ZGC is the main alternative to try when spark shows garbage collection pauses lining up with your lag. Nobody at GameServerBench has compared the two on a modpack, and no source read for this page publishes a side-by-side result for modded servers, so treat both flag sets below as starting points and use the spark method at the end to decide on your own server.

Should a modded Minecraft server use G1 or ZGC?

Use G1 unless spark shows that garbage collection pauses coincide with your lag, then test ZGC. That is a reading of the documents, not a measured result. Oracle's HotSpot guide for Java 25 says G1 "is selected by default on most hardware and operating system configurations" and is the pick when response time matters more than throughput. It describes ZGC as providing "max pause times under a millisecond, but at the cost of some throughput", and suggests it when response time is a high priority (Oracle GC tuning guide, Java 25, read 8 Nov 2026). The same guide says that unless you have strict pause-time requirements, you should first run the application and let the JVM pick.

Paper's Aikar's flags page (dated 18 Sep 2026) gives only a G1 flag set. This guide found no mention of ZGC on it. Neither Paper nor Oracle says anything about modpacks specifically.

The cost side of ZGC is in the documents too. Oracle says ZGC's main tuning knob is the maximum heap size and that "in general, the more memory you give to ZGC the better" (Oracle ZGC guide, Java 25). Space-Node adds that ZGC's collector threads use CPU the server could otherwise use, and that a heap with little headroom can cause allocation stalls. On a plan with little spare CPU or a small heap, that trade may not pay off. Whether it does on your pack is the thing to measure.

What is generational ZGC and which Java version does it need?

Generational ZGC separates young and old objects so that short-lived objects are collected more often, which suits a game that allocates heavily. The sequence in the OpenJDK record is:

  • ZGC became a product feature in JDK 15 (JEP 377).
  • Generational ZGC arrived in JDK 21, selected with -XX:+UseZGC -XX:+ZGenerational. Its stated goals are lower risk of allocation stalls, lower heap memory overhead and lower collection CPU overhead, with pauses staying under 1 ms (JEP 439). The JEP says the generational mode should be "a better solution for most use cases" and that the older mode could eventually be replaced.
  • JDK 23 made the generational mode the default for -XX:+UseZGC (JEP 474).
  • JDK 24 removed the non-generational mode and made ZGenerational an obsolete option (JEP 490). Oracle's Java 25 guide confirms: "As of JDK 24 ZGC is a generational garbage collector. The ZGenerational option has been removed."

JEP 490 says the obsolete option "will expire in a future release, at which point it will not be recognized by the HotSpot JVM, which will refuse to start." Space-Node, a hosting seller, reports that Java 26 and newer fail to start with -XX:+ZGenerational (Space-Node, updated 31 Oct 2026). This guide did not verify that on a running JVM. The practical rule: match the flag to your Java version, and do not copy a Java 21 line onto a newer runtime without checking the startup log.

Paper's getting-started page lists Java 21 for Minecraft 1.20 to 1.21.11 and Java 25 for 26.1 and later. Your Java version, which your pack and loader may constrain, decides which line applies.

Why are -Xms and -Xmx usually set to the same value?

Setting them equal gives the JVM a fixed-size heap, so it never has to grow or shrink it while the server runs. Three sources give reasons.

  • Aikar, the author of the flag set: if -Xms is lower than -Xmx you have "unused memory", G1 "operates better with the more memory it's given", and the server should always be expected to use all of -Xmx (Aikar's 2018 post, last updated 25 Apr 2020; repeated on Paper's page). He calls equal settings "industry wide accepted information" under G1. That is his claim, and the post gives no measurements.
  • Oracle, for any Java server: setting them equal "increases predictability by removing the most important sizing decision from the virtual machine. However, the virtual machine is then unable to compensate if you make a poor choice" (HotSpot GC tuning guide, Java 21).
  • Oracle, for ZGC: ZGC does not uncommit memory below -Xms, so equal values disable uncommit. If low latency is the reason you run ZGC, Oracle suggests equal values plus -XX:+AlwaysPreTouch to page memory in before the application starts (Java 25 ZGC guide).

The catch is the memory outside the heap. Paper's page says Java needs more than -Xmx, advises cutting -Xms and -Xmx by about 1000 to 1500 MB below the memory your host gives you, and gives the example that a host plan of 8 GB should use 6500 MB. It also recommends "at least 6-10GB, no matter how few players" and says 32 GB "will only waste your money". That is Paper's guidance for G1 on Paper, not for a particular pack. For how much heap a pack asks for, see ATM10 server RAM requirements.

Which flag sets can I copy?

Two sets are worth knowing. Each is attributed to its author, and GameServerBench has run neither.

Aikar's G1 flags. Written by Aikar, published in 2018 and last updated by him on 25 Apr 2020 for Minecraft 1.15-era servers, and hosted today on Paper's docs. The example below is for a 10 GB heap, and the only things to change are -Xms and -Xmx (and the jar name). Do not set them to your host's full memory.

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 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -jar paper.jar --nogui

Aikar's original post says that with a heap above 12 GB, you change five values: G1NewSizePercent=40, G1MaxNewSizePercent=50, G1HeapRegionSize=16M, G1ReservePercent=15 and InitiatingHeapOccupancyPercent=20, and to revert if old-generation collections increase. Paper links a start-script generator from the same page. These flags were written for Paper and Spigot servers. Nothing in the post or on Paper's page says they were tuned on modded packs.

A generational ZGC set from Space-Node. Space-Node sells Minecraft hosting, so this is a vendor's recommendation. Its guide (published 15 Apr 2026, updated 31 Oct 2026) gives these lines for a 16 GB server with a 14 GB heap. Replace all G1 options with one of them:

Java 25 and newer, per Space-Node:

-Xms14G -Xmx14G -XX:+UseZGC -XX:+AlwaysPreTouch -XX:+DisableExplicitGC

Java 21, per Space-Node:

-Xms14G -Xmx14G -XX:+UseZGC -XX:+ZGenerational -XX:+AlwaysPreTouch -XX:+DisableExplicitGC

Space-Node says it keeps G1 with Aikar's flags below 12 GB and suggests testing ZGC from 12 GB up when GC pauses match lag. Its own text calls that threshold "our own starting point, not a JDK limit" and calls its heap table "starting points ... not benchmark results". It also warns that leaving -XX:+UseG1GC next to -XX:+UseZGC stops Java from starting. The AlwaysPreTouch and equal-heap pairing matches Oracle's ZGC note above, and DisableExplicitGC is the same flag Aikar explains as blocking plugin-triggered full collections.

Has anyone compared them on modded servers?

Not in anything read for this page. Space-Node's guide and its load-test method describe how to test and state that G1 with Aikar's flags is the usual default, but give no before-and-after tables. Aikar's post reports "great improvement" from users and no numbers. GameServerBench plans A/B runs on All the Mods 10 and one other pack, with raw spark exports, but no server exists and nothing has been run. The planned approach is on the method page.

How do I compare G1 and ZGC on my own server with spark?

Keep everything fixed except the flags, repeat each configuration, and judge by tick-time spread rather than average TPS. The steps:

  1. Record the setup. Pack and version, loader version, Java version, the full flag line, view distance, simulation distance. Space-Node's load-test guide lists the same items.
  2. Freeze the world. Copy the world and restore it before each run so every run starts from the same state.
  3. Check whether GC is your problem first. spark's /spark gc prints garbage collection history, /spark gcmonitor toggles GC monitoring, and /spark tickmonitor reports ticks that go over a threshold, including GC activity unless you pass --without-gc (spark commands). If long ticks do not line up with collections, changing collector is unlikely to help.
  4. Capture the same activity each run. Start /spark profiler start --timeout 600 while your usual players do their usual things. Paper's profiling page uses the same ten-minute command. Run /spark tps for the MSPT figures and /spark health for CPU and memory.
  5. Change only the flags (and Java version, if ZGC needs it). Then repeat step 4 at least three times per configuration. Three is this site's suggestion, not a standard; more runs are better.
  6. Compare the 95th percentile and the worst ticks, not only the median. spark reports min, median, 95th percentile and max MSPT (spark docs); MSPT and TPS explained covers how to read them. Also watch CPU in /spark health, because concurrent collection costs CPU.
  7. Keep GC logging on. Aikar says GC logging "does not hurt your performance" and can stay on; for Java 11 and later the flag is -Xlog:gc*:logs/gc.log:time,uptime:filecount=5,filesize=1M.

Idle bot players are a poor stand-in for real play on a modded server; how to benchmark your own game server covers that and the host-terms check you need before any synthetic load. If your host controls the startup line, ask support whether custom JVM arguments are allowed before you change them.

Next step: if spark points at mods or machines instead of GC, read ATM10 server lag and high MSPT, or return to the guides hub.

Sources

  1. Aikar's flags - PaperMC Docs (page dated 18 Sep 2026)PrimaryAccessed 8 Nov 2026
  2. Getting started - PaperMC Docs (page dated 18 Sep 2026)PrimaryAccessed 8 Nov 2026
  3. Tuning the JVM - G1GC Garbage Collector Flags for Minecraft - Aikar's ThoughtsPrimaryAccessed 8 Nov 2026
  4. Factors Affecting Garbage Collection Performance - HotSpot GC Tuning Guide, Java 21PrimaryAccessed 8 Nov 2026
  5. Available Collectors - HotSpot GC Tuning Guide, Java 25PrimaryAccessed 8 Nov 2026
  6. The Z Garbage Collector - HotSpot GC Tuning Guide, Java 25PrimaryAccessed 8 Nov 2026
  7. JEP 377: ZGC: A Scalable Low-Latency Garbage Collector (Production)PrimaryAccessed 8 Nov 2026
  8. JEP 439: Generational ZGCPrimaryAccessed 8 Nov 2026
  9. JEP 474: ZGC: Generational Mode by DefaultPrimaryAccessed 8 Nov 2026
  10. JEP 490: ZGC: Remove the Non-Generational ModePrimaryAccessed 8 Nov 2026
  11. spark command usage - spark docsPrimaryAccessed 8 Nov 2026
  12. TPS and MSPT - spark docsPrimaryAccessed 8 Nov 2026
  13. Profiling - PaperMC DocsPrimaryAccessed 8 Nov 2026
  14. ZGC Garbage Collector for Minecraft: ZGC vs G1GC 2026 - Space-Node (hosting seller)PrimaryAccessed 8 Nov 2026
  15. The Quest for 20 TPS: How to Load Test a Modded Minecraft Server - Space-Node (hosting seller)PrimaryAccessed 8 Nov 2026
Esc

Arrow keys to move, Enter to open.