Best JVM Arguments for a Modded Minecraft Server
Updated July 28, 2026
For a modded Minecraft server, the proven recipe comes down to three things: the Java version your game version requires (Java 8 up to 1.16.5, Java 17 for 1.18 through 1.20.4, Java 21 for 1.20.5 through 1.21.x, Java 25 from Minecraft 26.1), -Xms and -Xmx set to the same value, and Aikar’s flags — a G1 garbage collector tuning built for Minecraft that has been the community standard since 2018 and remains the best general-purpose baseline in 2026. Generational ZGC only becomes worthwhile on very large allocations, from about 16 GB up.
A lot of folklore orbits that recipe: “magic flag” lists copied from forum to forum, the belief that doubling RAM doubles performance, arguments inherited from Java 8 applied to Java 21. This guide gives the complete flags, explains what they actually do, says when to deviate — and above all how to verify by measurement, with spark, rather than by superstition.
The right Java version comes first
No flag will rescue a server launched on the wrong Java: check this first, and note that the constraint comes from the Minecraft version, not the modpack. Prefer Adoptium’s Temurin builds, free and license-trap-free, and mind both directions: a 1.12.2 pack rejects Java 17 just as surely as a 1.20.5 pack rejects Java 8.
- Minecraft 1.7.10 through 1.16.5 (RLCraft, GTNH, Create: Above and Beyond…): Java 8 — GregTech: New Horizons is the exception, its modernized 1.7.10 runs on Java 17 to 25.
- Minecraft 1.17: Java 16, a transition version quickly left behind.
- Minecraft 1.18 through 1.20.4 (Create Astral, BMC4, Prominence II…): Java 17.
- Minecraft 1.20.5 through 1.21.x (All The Mods 10, BMC5…): Java 21.
- Minecraft 26.1 and beyond (the new 2026 version scheme): Java 25.
Aikar’s flags: the baseline that works
Aikar, a developer in the Paper ecosystem, published a set of arguments in 2018 that tunes the G1 garbage collector for Minecraft’s very particular memory profile: enormous numbers of ultra-short-lived objects (one tick creates them, the next throws them away). Untuned, the GC lets that work pile up and then takes big pauses — the familiar periodic freezes where TPS tanks. Aikar’s flags force small, frequent collections that go unnoticed. Written for Paper, they apply just as well to a Forge, NeoForge or Fabric server: a modded server has the same memory behavior, only hungrier.
The full line for a server with 10 GB, to adapt to your allocation: 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 nogui
The load-bearing pieces: UseG1GC enables the G1 collector; G1NewSizePercent and G1MaxNewSizePercent reserve 30 to 40 percent of the heap for the young generation, where a tick’s objects live and die; MaxGCPauseMillis=200 caps each pause under the length of four ticks; AlwaysPreTouch claims all the RAM at startup instead of on the fly; PerfDisableSharedMem avoids disk-write hiccups; MaxTenuringThreshold=1 discards garbage early instead of copying it from collection to collection. From 12 GB allocated and up, Aikar recommends a variant: G1NewSizePercent=40, G1MaxNewSizePercent=50, G1HeapRegionSize=16M, G1ReservePercent=15 and InitiatingHeapOccupancyPercent=20.
-Xms = -Xmx: why the rule exists
-Xmx sets the JVM’s memory ceiling, -Xms its starting size. On a machine dedicated to the game, set both to the same value: memory you do not give Minecraft serves no one else, and a fixed-size heap spares G1 the busywork of growing and shrinking — combined with AlwaysPreTouch, everything is claimed cleanly at startup. The only exception is a shared machine where other services genuinely need that memory.
Also keep headroom for the operating system: the JVM consumes memory beyond the heap (metadata, threads, mods’ native memory). On a 16 GB machine, allocate 10 to 12 GB, not 15 — a server that pushes the system into swap becomes unplayable far more surely than one running a slightly tight heap.
G1GC or generational ZGC: who actually needs ZGC?
Since Java 21, ZGC has a generational mode — and on recent JDKs, enabling ZGC (-XX:+UseZGC) gives you that mode out of the box. Its promise: sub-millisecond pauses regardless of heap size, where G1, even well tuned, still lets pauses of a few dozen milliseconds through on very large allocations. The price: roughly 10 to 15 percent more CPU spent on GC, plus a small memory overhead.
The practical conclusion is simple. Up to 12 GB of allocation — which covers nearly every modded server — G1 with Aikar’s flags remains the better trade: pauses are already imperceptible and the CPU saved goes back into server ticks. Generational ZGC wins on outlier setups: 16, 24, 32 GB heaps for a big public server on a heavy pack, with CPU cores to spare. If that is you, replace all the G1 flags with -XX:+UseZGC (keeping Xms=Xmx and AlwaysPreTouch) and compare the result under real load with spark — the verdict that counts is measured, not theoretical.
The classic traps
- Too much RAM: past what you need, every collection churns a bigger heap for nothing. Going from 10 to 20 GB does not speed up a server that only uses 8 — it lengthens the GC’s workload.
- Magic flags copied without understanding: the exotic lists floating around forums often stack obsolete, contradictory or simply ignored arguments on modern JVMs. A sane, measured baseline beats a mystical pile.
- Client flags on a server: tweaks meant to raise a modded client’s FPS have no business in a server script, and vice versa.
- GraalVM as a miracle cure: its JIT compiler can bring a modest gain on some packs, but it is a marginal optimization to try last — after the correct Java version, sound GC flags and profiling out the real lag sources.
- Changing three things at once: change one parameter, measure for a day, conclude. Otherwise you will never know what did what.
Measure with spark instead of believing
The habit that separates well-run servers from the rest: install spark (Forge, NeoForge, Fabric — free and nearly overhead-free) and look at the numbers before touching flags. /spark tps shows TPS and MSPT (milliseconds per tick), /spark gcmonitor logs every GC pause in real time, /spark profiler produces an interactive report showing exactly where each tick’s time goes.
The reading is unambiguous: if GC pauses are rare and short but MSPT sits above 50 ms, your problem is not the JVM — it is a mob farm, a modded machine or an overloaded chunk, and the profiler will name it. Java arguments eliminate memory hiccups; they do not buy tick time. Set the flags once, correctly, then spend your energy where spark points.
Mods & Tools Mentioned
- Aikar’s flags — original article ↗ — The reference explanation, flag by flag, by their author.
- Aikar’s flags — PaperMC docs ↗ — The maintained version, with the full command ready to copy.
- spark ↗ — Profiler, GC monitor and online report viewer.
- spark on Modrinth ↗ — The mod download for Forge, NeoForge, Fabric and Quilt.
- Minecraft Performance Flags Benchmarks ↗ — Modern flags (G1, ZGC, GraalVM) backed by benchmarks rather than rumor.
- Adoptium (Eclipse Temurin) ↗ — Free OpenJDK builds: Java 8, 17, 21 and 25 depending on your game version.
Frequently Asked Questions
› Do Aikar’s flags work on a Forge or NeoForge server?
Yes. They were written for Paper, but they tune the JVM’s garbage collector, not the server software: a modded server has the same memory profile, only more demanding, and benefits just as much. On recent Forge/NeoForge packs, put them in user_jvm_args.txt.
› Why set -Xms equal to -Xmx?
A fixed-size heap spares G1 the grow-and-shrink cycles, and with AlwaysPreTouch all the memory is claimed at startup: no allocation micro-stutters mid-game. On a dedicated server, unallocated RAM would sit idle anyway.
› Does more RAM make the server faster?
No — past actual need it is the opposite: the GC churns a larger heap for the same work. Size to the pack (6 to 10 GB covers most cases), check real usage with spark, and leave headroom for the operating system.
› When should I switch to generational ZGC?
From about 16 GB of heap, on Java 21 or newer, with CPU to spare — typically a big public server on a heavy pack. Below that, G1 with Aikar’s flags already delivers imperceptible pauses at a lower CPU cost. Either way, decide by comparing under real load with spark.
› Is GraalVM worth it for a modded server in 2026?
It is a marginal bonus, not a revolution: its JIT compiler can shave a few percent on some packs. Nail the essentials first — correct Java version, sane GC flags, profiled lag sources — and test GraalVM last, with measurements in hand.
› How do I tell whether my lag comes from GC or from a mod?
With spark: /spark gcmonitor logs GC pauses, /spark tps shows MSPT. Long, frequent GC pauses point at the JVM — revisit flags and allocation. High MSPT with a quiet GC points at content: run /spark profiler to identify the mod, farm or chunk responsible.
Read Next
Ready to play? Find an active server in the rankings.
See the rankings