DGuard

JVM flags for a Minecraft server: what to use and why

A server's start command is a Java version, an amount of memory and, optionally, garbage collector settings. Here's what Paper's docs advise: equal -Xms and -Xmx, Aikar's flags for G1 and what each of them does, plus when large pages and ZGC are worth trying.

The DGuard team9 min read

In short: install the Java your Paper version requires (25 for 26.1 and newer, 21 for 1.20–1.21.11), set -Xms and -Xmx to the same value, and leave the OS 1000–1500 MB on top of the heap. Paper's start script generator suggests trying no flags at all first on a modern Java, and switching to Aikar's flags if you run into garbage collection problems. ZGC and large pages are experiments: try them only when a measurement shows a problem, and check the result.

Which Java you need

Paper versionJava
1.17–1.1917
1.20–1.21.1121
26.1 and newer25
From the Getting started table in Paper's docs

Run the server on an older Java and it fails with UnsupportedClassVersionError — see logs and crash reports. Installing Java on Ubuntu or Debian is covered in a Minecraft server on Linux.

Memory: -Xms, -Xmx and headroom

  • `-Xmx` is the heap ceiling, `-Xms` is what's allocated at startup. Paper's docs ask you to set them equal: the server has to be able to use the whole -Xmx anyway, and memory between the two just sits unused. G1 works better the more memory it's given.
  • Don't give the heap all your memory. The Java process needs more than -Xmx: Paper suggests going 1000–1500 MB below the machine's or plan's memory — around 6500 MB on 8 GB. Otherwise the OOM killer may kill the process.
  • Without `-Xmx`, Java picks the ceiling itself — 25% of available memory by default (-XX:MaxRAMPercentage). That's usually too little for a server, so set it explicitly. In the itzg/minecraft-server Docker image the MEMORY variable does it — see Minecraft in Docker.

How much memory your player count needs, and how to measure usage, is in a separate article: how much RAM a server needs.

Aikar's flags: what each one does

Aikar's flags are a set of G1 garbage collector settings tuned for Minecraft's load. PaperMC's start script generator inserts the same set. Per Paper's docs, Minecraft allocates memory extremely fast — at least 800 MB per second on a 30-player server, mostly short-lived objects — and the flags aim to let those objects die young instead of cluttering the old generation.

FlagWhy, per Paper's docs
-XX:+UseG1GCselects the G1 collector; the other flags tune it
-XX:+UnlockExperimentalVMOptionsunlocks some of the flags below
-XX:G1NewSizePercent=30, -XX:G1MaxNewSizePercent=40more room for the young generation, so young collections run less often
-XX:MaxTenuringThreshold=1, -XX:SurvivorRatio=32transient data isn't promoted to the old generation, and the freed space goes to new objects
-XX:G1ReservePercent=20reserve memory against running out of space while copying objects (default 10)
-XX:G1HeapRegionSize=8Mbigger regions: objects of half a region or more count as "humongous" and go straight to the old generation
-XX:MaxGCPauseMillis=200a pause goal of at most 200 ms, about 4 ticks
-XX:+DisableExplicitGCstops plugins from triggering a full collection, which causes a big lag spike
-XX:+AlwaysPreTouchsets up and reserves the whole heap at startup rather than as it grows
-XX:+ParallelRefProcEnabledprocesses weak references on multiple threads
-XX:G1MixedGCLiveThresholdPercent=90, -XX:G1MixedGCCountTarget=4, -XX:G1RSetUpdatingPauseTimePercent=5the old generation is cleaned in small, frequent steps, with less work inside the pause
-XX:+PerfDisableSharedMemstops JVM statistics being written to a file, which causes latency under heavy disk IO
The set also includes -XX:InitiatingHeapOccupancyPercent=15, -XX:G1HeapWastePercent=5 and the markers -Dusing.aikars.flags, -Daikars.new.flags. Change the set as a whole, not flag by flag.

The start script

start.sh for Linux: Aikar's flags and auto-restart, as in PaperMC's generator
#!/usr/bin/env sh
# an 8 GB machine or plan: 6500 MB for the heap, the rest for Java itself and the OS
while [ true ]; do
    java -Xms6500M -Xmx6500M -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+ParallelRefProcEnabled -XX:+PerfDisableSharedMem -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1HeapRegionSize=8M -XX:G1HeapWastePercent=5 -XX:G1MaxNewSizePercent=40 -XX:G1MixedGCCountTarget=4 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1NewSizePercent=30 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:G1ReservePercent=20 -XX:InitiatingHeapOccupancyPercent=15 -XX:MaxGCPauseMillis=200 -XX:MaxTenuringThreshold=1 -XX:SurvivorRatio=32 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -jar paper.jar --nogui
    echo Server restarting...
    echo Press CTRL + C to stop.
done
  1. 1

    Set the memory

    -Xms and -Xmx equal, 1000–1500 MB below the machine's or plan's memory.

  2. 2

    Check the jar name

    Replace paper.jar with your file's name — or rename the file so you don't have to edit the script on every update.

  3. 3

    Make the script executable

    chmod +x start.sh, then ./start.sh. The while loop restarts the server after stop or a crash; to stop for good, press Ctrl+C in the console.

If systemd runs the server with Restart=always, you don't need the loop — a single exec java ... line is enough, as in a Minecraft server on Linux. For Velocity the generator offers its own short set: -XX:+AlwaysPreTouch -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1HeapRegionSize=4M -XX:MaxInlineLevel=15.

The garbage collection log

To see whether the collector is behind the freezes, turn on its log. Per Paper's docs it doesn't hurt performance and takes about 5 MB, so it can stay on. For Java 11 and newer add the flag below to the start command. For a quicker look, use the built-in spark: /spark gc shows the history, /spark gcmonitor watches collections — more in the spark profiler.

GC log — written to logs/gc.log, up to 5 files of 1 MB
-Xlog:gc*:logs/gc.log:time,uptime:filecount=5,filesize=1M

Large pages and transparent huge pages

Large memory pages ease pressure on the CPU's address cache, which can help memory-hungry applications. But Oracle's documentation warns: memory pinned for them can starve other programs of regular memory, and on a long-running system fragmentation can make it impossible to reserve enough — then the JVM falls back to regular pages.

  • `-XX:+UseLargePages` — real large pages, off by default. On Linux they must be pre-allocated in the system (nr_hugepages); check support with grep Huge /proc/meminfo.
  • `-XX:+UseTransparentHugePages` — Linux only, pages the system allocates on its own. Oracle calls the option experimental, Paper calls it controversial: try it if you can't set up real large pages, and only together with -XX:+AlwaysPreTouch. PaperMC says it hasn't measured how this affects Minecraft.

ZGC: when it's worth a try

ZGC is a low-latency collector: per Oracle's documentation, its pauses are a few milliseconds and don't depend on heap size, at some cost to throughput. It works well as long as the machine has enough memory and CPU to reclaim memory faster than the application allocates it, as JEP 439 puts it. Paper's docs don't mention ZGC — they recommend G1, with no flags or with Aikar's — so ZGC remains an experiment for those whose G1 pauses are noticeable and who have spare cores.

JavaHow to enable generational ZGC
21-XX:+UseZGC -XX:+ZGenerational — without the second flag you get the old non-generational mode
23-XX:+UseZGC — generational mode is the default
24 and newer, including 25-XX:+UseZGC — the non-generational mode is gone, and -XX:+ZGenerational is no longer needed
From JEP 439 (Java 21), JEP 474 (Java 23) and JEP 490 (Java 24)
Java 25: ZGC instead of G1 — no Aikar's flags with it
java -Xms6500M -Xmx6500M -XX:+UseZGC -jar paper.jar --nogui

Aikar's flags are G1 settings, so don't mix them with ZGC: pick one collector. Compare fairly — the same player count, /spark tps and the GC log before and after — and go back to G1 if things got worse.

JVM flags and the anticheat

Long garbage collection pauses are freezes for every player, and time-based checks see things during freezes that didn't happen. DGuard — the best anticheat for a modern Minecraft server — gathers evidence across several episodes and doesn't punish a single odd moment. It needs nothing added to the start command: the key lives in plugins/DGuard/config.yml, and checks run on network threads — the main thread cost is 0.03% of one core. It runs on Paper and Leaf 1.21–26.3, with whatever Java your server version requires.

FAQ

Do I need Aikar's flags on Java 21 or 25?

Not necessarily. Paper's script generator suggests trying no flags first on a modern Java, and using Aikar's flags if you run into garbage collection problems.

Why should -Xms equal -Xmx?

Paper's docs advise it: the server has to be able to use the whole -Xmx anyway, and memory between -Xms and -Xmx sits unused. G1 works better when it gets the memory up front.

Which Java does Minecraft 26.x need?

Java 25: per Paper's docs it's required for 26.1 and newer. 1.20–1.21.11 use Java 21.

Is ZGC better than G1 for a Minecraft server?

Paper's docs don't say so: they recommend G1, with no flags or with Aikar's. ZGC gives short pauses at some cost to throughput; treat it as an experiment and compare with measurements.

Can I give Java all of the server's memory?

No. The Java process needs more than -Xmx, and the OS needs memory too. Paper suggests setting -Xmx 1000–1500 MB below the machine's or plan's memory.

Sources

All sections

Run the best anticheat for modern Minecraft

DGuard catches all 47 kinds of cheats in our comparison — the closest rival, Intave, has 27. Full protection on every plan, free up to 30 players online.