What is actually happening when the server freezes
Java does not free memory as it goes. It allocates into a heap, and periodically a garbage collector identifies what is no longer referenced and reclaims it. Some of that work requires stopping the application entirely — every thread, including the tick loop — for the duration of the pass.
On a small, well-shaped heap those pauses are a few milliseconds and invisible. On a large or untuned one they can run into hundreds of milliseconds, which is several ticks. The server does not lose TPS in any sustained way; it simply stops existing for a moment, then resumes. That is precisely the symptom players report as "random lag spikes" on a server that otherwise reads as perfectly healthy — and it is why the spark profile comes back clean.
The goal is short pauses, not fast collection
This is the key idea and it is counter-intuitive. You are not trying to make garbage collection *cheaper* — total GC work is roughly fixed by how much garbage your server produces. You are trying to change its distribution: many small pauses of a few milliseconds instead of occasional pauses of half a second. The total time spent collecting may even go up slightly. Players will never notice a 5 ms pause and always notice a 400 ms one.
Every recommendation below follows from that single objective.
The flags that matter
| `-Xmx` | Maximum heap. Set it to what the server needs, not what the machine has — leave 1–2 GB for the JVM's non-heap memory and the OS |
| `-Xms` | Starting heap. Set it equal to `-Xmx` on a dedicated server so the JVM never resizes the heap under load |
| `-XX:+UseG1GC` | Selects the G1 collector, which is designed around pause-time targets — the right shape for a game server |
| `-XX:MaxGCPauseMillis` | The pause target you are asking G1 to aim for. This is the flag that expresses the whole goal |
| `-XX:+AlwaysPreTouch` | Touches the heap at startup so pages are committed up front rather than during play. Slower boot, smoother running |
| `-XX:+DisableExplicitGC` | Stops badly-behaved code from forcing a full collection at an arbitrary moment |
Beyond these, the widely-used Aikar's flags set tunes G1's internal region and threshold behaviour specifically for Minecraft workloads. Using that set is a reasonable default and a large improvement over running untuned — take it from its own documentation rather than copying a decade-old forum post, and note that the recommended values differ once your heap is large, which is a detail people routinely miss when pasting flags for a heap size the flags were never written for.
Right-size the heap first
Tuning the collector on an oversized heap is fixing the wrong problem. A server given 16 GB it never needed will have long pauses no flag set fully rescues, because you have handed the collector an enormous area to sweep. Work out what the server actually needs — the RAM guide covers realistic figures — set `-Xmx` there, set `-Xms` to match, and *then* tune.
The corollary matters too: do not allocate the machine's entire physical memory. The heap is not the process's total footprint, and a JVM whose heap plus overhead exceeds available memory gets killed by the kernel rather than merely being slow.
Flags to stop copying
- Anything you cannot explain. If you cannot say what a flag does, you cannot tell whether it helped, and you will carry it between servers forever.
- Flags for a different collector. Mixing options from unrelated collectors is at best ignored and at worst a startup failure.
- Ancient forum flag soups. Many predate the current JVM by a decade, and some now disable behaviour you want.
- Flags matched to the wrong heap size. Tuning values written for a 4 GB heap are not correct at 16 GB, and the difference is not cosmetic.
- `-Xmx` set to total system RAM. Covered above, and the most common single mistake in this area.
Verify it worked
Change the flags, restart, and then measure rather than asking whether it feels better. `/spark health` reports GC activity directly; what you want to see is pause durations dropping even if collections become more frequent. If pauses are unchanged, the heap is probably still the wrong size, or the freeze was never GC in the first place — in which case go back to profiling and find the plugin that is genuinely responsible.
One useful reality check: if your "lag spikes" correlate with player joins rather than with time, this is not garbage collection. Bursts of joins that stall a server are a chunk-loading pattern — or, if the joins are not real players, something else entirely.
What tuning cannot do
No flag set survives a join-flood. Thousands of fake connections allocate objects far faster than any collector can reclaim them, the heap fills, pauses stretch, and the server either freezes solid or dies with an out-of-memory error — which looks exactly like a tuning failure and is not one. The traffic should never have reached the JVM. That is filtered at the edge, before a connection becomes a player object at all, and it is the one lag cause that no amount of local tuning can reach.
Common questions
Why does my Minecraft server freeze every few minutes?+
Almost always garbage collection. Java reclaims unused memory in periodic passes, and on a large or badly-shaped heap those passes take long enough to stall the server entirely — which players experience as a brief total freeze on a server that is otherwise perfectly smooth. Confirm it with /spark health, which reports GC activity alongside TPS.
Should -Xms and -Xmx be the same value?+
Yes, for a dedicated Minecraft server. Setting them equal makes the JVM claim the whole heap at startup instead of growing it under load, which removes a class of pause caused by heap resizing at exactly the moment the server is busiest. The memory was reserved for the server anyway, so there is nothing to gain from letting it grow gradually.
What are Aikar's flags and should I use them?+
They are a widely-used G1GC tuning set, developed for Minecraft workloads, that biases the collector toward frequent short pauses rather than rare long ones. They are a sensible default for most servers and far better than running untuned. They are not magic — they do not fix a plugin eating your tick, and on very large heaps the recommended values differ from the standard set.
Does more RAM fix garbage collection pauses?+
Usually it makes them worse. A larger heap gives the collector more to scan, so pauses become less frequent but longer — which is the opposite of what a game server wants. If you are freezing periodically, the fix is tuning the collector's behaviour and right-sizing the heap, not raising -Xmx.
Which Java version should I run a Minecraft server on?+
Run the version your server software requires — modern Minecraft versions need a modern Java, and running below the requirement simply will not start. Beyond that, newer JVMs generally bring better garbage collection, so there is rarely a reason to stay on an older release once your software supports the newer one.
Tuned properly and still falling over when it matters? Start free with Arvoris — antibot and L4/L7 mitigation on every plan, so the flood never reaches your heap.