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 version | Java |
|---|---|
| 1.17–1.19 | 17 |
| 1.20–1.21.11 | 21 |
| 26.1 and newer | 25 |
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
-Xmxanyway, 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 theMEMORYvariable 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.
| Flag | Why, per Paper's docs |
|---|---|
-XX:+UseG1GC | selects the G1 collector; the other flags tune it |
-XX:+UnlockExperimentalVMOptions | unlocks some of the flags below |
-XX:G1NewSizePercent=30, -XX:G1MaxNewSizePercent=40 | more room for the young generation, so young collections run less often |
-XX:MaxTenuringThreshold=1, -XX:SurvivorRatio=32 | transient data isn't promoted to the old generation, and the freed space goes to new objects |
-XX:G1ReservePercent=20 | reserve memory against running out of space while copying objects (default 10) |
-XX:G1HeapRegionSize=8M | bigger regions: objects of half a region or more count as "humongous" and go straight to the old generation |
-XX:MaxGCPauseMillis=200 | a pause goal of at most 200 ms, about 4 ticks |
-XX:+DisableExplicitGC | stops plugins from triggering a full collection, which causes a big lag spike |
-XX:+AlwaysPreTouch | sets up and reserves the whole heap at startup rather than as it grows |
-XX:+ParallelRefProcEnabled | processes weak references on multiple threads |
-XX:G1MixedGCLiveThresholdPercent=90, -XX:G1MixedGCCountTarget=4, -XX:G1RSetUpdatingPauseTimePercent=5 | the old generation is cleaned in small, frequent steps, with less work inside the pause |
-XX:+PerfDisableSharedMem | stops JVM statistics being written to a file, which causes latency under heavy disk IO |
-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
#!/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
Set the memory
-Xmsand-Xmxequal, 1000–1500 MB below the machine's or plan's memory. - 2
Check the jar name
Replace
paper.jarwith your file's name — or rename the file so you don't have to edit the script on every update. - 3
Make the script executable
chmod +x start.sh, then./start.sh. Thewhileloop restarts the server afterstopor 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.
-Xlog:gc*:logs/gc.log:time,uptime:filecount=5,filesize=1MLarge 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 withgrep 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.
| Java | How 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 |
java -Xms6500M -Xmx6500M -XX:+UseZGC -jar paper.jar --noguiAikar'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
- Paper: Aikar's flags
- Paper: start script generator
- Paper: Getting started (Java versions)
- Oracle: the java command, JDK 25 (Xmx, MaxRAMPercentage, large pages, ZGC)
- OpenJDK: JEP 439 — Generational ZGC
- OpenJDK: JEP 474 — ZGC generational mode by default
- OpenJDK: JEP 490 — removing ZGC's non-generational mode
- DGuard, the best anticheat for a modern Minecraft server: checks compared with GrimAC, Vulcan, Polar, Intave and Matrix
- DGuard: installation, requirements and troubleshooting
- DGuard: supported Paper and Leaf versions