Best JVM Flags for a Minecraft Server

Guides
19 July 2026 · By Josh P
Best JVM Flags for a Minecraft Server

Twenty players are crammed into spawn for your server's first big event, farms ticking, mobs churning, chunks loading as latecomers sprint in. Then everything freezes for half a second, and when it comes back everyone has snapped a few blocks backwards. Chat fills with "lag?", but nobody's ping moved.

That freeze is almost never your CPU running out of power. It is Java stopping the entire server to clean up memory, and it hits hardest at exactly the busy moments you most want it to hold together. This guide explains what JVM flags actually do, why the defaults are not good enough, and the exact flags that fix it.


What a "JVM flag" actually is

Minecraft (Java Edition) runs inside the Java Virtual Machine, the same way a game runs inside a game engine. When you start a server, you are really starting java, and pointing it at the server jar. JVM flags are startup options for java itself, not for Minecraft. They control how much memory Java is allowed to use, which garbage collector it uses, and dozens of smaller behaviours around threading and memory layout. None of this touches your server.properties or any plugin config. It is a layer underneath all of that.

Run java -jar server.jar with no flags and Java will pick conservative defaults meant to work on everything from a phone to a supercomputer. Those defaults are not tuned for a process that needs to respond every 50 milliseconds, 24 hours a day, with a memory access pattern as chaotic as Minecraft's.


Garbage collection is the actual villain

Java manages memory for you. Every time your server spawns an entity, loads a chunk, or sends a packet, it allocates a small object in memory. It never has to free that memory manually, because a background process called the garbage collector does it automatically. That sounds convenient, and it is, until the garbage collector needs to pause the entire server to do its job properly.

In player terms: a GC pause is why the whole server freezes for a fraction of a second, or sometimes a full second or more, and every player sees the exact same stutter at the exact same moment. It is not lag in the network sense, ping has not changed, the server has simply stopped doing anything while it tidies up memory. A server under heavier load (more players, more entities, a bigger modpack) allocates memory faster, which means the garbage collector runs more often and works harder each time. That is why the exact same player count feels worse on a busy Friday night than a quiet Tuesday afternoon, more is happening per tick, so more garbage is being created per tick.

The goal of JVM tuning is not to stop garbage collection, that is not possible. The goal is to make each pause shorter and more predictable, so it stays under the radar instead of showing up as a stutter.


The flags that actually matter

-Xmx and -Xms, and why they should match. -Xmx sets the maximum heap size, -Xms sets the starting heap size. If they are different, Java resizes the heap while your server is running, and a heap resize is itself a pause. Setting -Xms equal to -Xmx allocates the full amount up front, so that resize never has to happen mid game. The tradeoff is a slightly longer startup, which is a fair trade for a server that stays up for weeks.

-XX:+UseG1GC, the garbage collector itself. G1 (Garbage First) breaks the heap into regions and collects the emptiest ones first, which keeps individual pauses short even on a large heap. It has been Java's default collector for years, but the flags below are what turn "the default collector" into "a collector tuned specifically for a real time game server".

-XX:MaxGCPauseMillis=130, the pause time target. This tells G1 to aim for pauses no longer than 130 milliseconds, and to size its internal regions to hit that target. It is a goal, not a hard guarantee, but it is the single biggest lever for turning a noticeable freeze into something players do not consciously register.

-XX:G1NewSizePercent and -XX:G1HeapRegionSize, how memory is carved up. Minecraft allocates huge numbers of short lived objects, packets, block updates, pathfinding data, most of which are garbage within a second. A larger young generation (G1NewSizePercent) means more of that short lived churn gets cleaned up in cheap, fast collections before it ever reaches the expensive full heap scan. G1HeapRegionSize sets how finely the heap is sliced, larger servers benefit from larger regions.

-XX:InitiatingHeapOccupancyPercent=10 and -XX:G1ReservePercent=20, staying ahead of trouble. Starting the concurrent marking cycle early, at only 10% heap occupancy, gives G1 time to plan its next collection properly instead of being forced into a rushed, expensive one. Reserving 20% of the heap as a safety margin avoids the worst case scenario, a "to space exhausted" event, where G1 has no choice but to fall back to a full stop the world collection that can pause the server for several seconds.

-XX:+DisableExplicitGC. Some plugins and mods call System.gc() directly, which by default forces an immediate full garbage collection regardless of what G1 was planning. This flag ignores those calls and leaves memory management to G1, where it belongs.

-XX:+AlwaysPreTouch. Faults in every page of heap memory at startup instead of lazily as it is first used. It adds a little to boot time in exchange for removing memory allocation stutter during the first few minutes after a restart, exactly the moment players are logging back in.

-XX:ReservedCodeCacheSize. Java's JIT compiler turns frequently run code into fast native machine code and stores it in the code cache. A modded server with hundreds of mod classes needs more room here than a vanilla one, if the cache fills up, Java has to evict and recompile code, which costs CPU exactly when you can least afford it.

There are more flags in the full set below, thread priority hints, NUMA awareness, survivor ratio tuning, that each shave a smaller amount off the total. None of them matter as much as the ones above, but together they add up.


Where these numbers actually come from

For a long time, "Aikar's flags" were the community standard, and they are still a solid baseline. The set below is a newer, independently benchmarked evolution of that lineage from brucethemoose/Minecraft-Performance-Flags-Benchmarks, a public project that actually measures TPS and pause times across flag combinations on modern JDKs, rather than relying on received wisdom. These are the same flags MCHosts runs in production, we did not write a separate marketing set, this is what actually launches your server.


The full flag sets

MCHosts scales these automatically based on your plan's RAM and picks the right JDK for your Minecraft version behind the scenes, see our server types guide if you want the JDK to version mapping. If you are self hosting or running Java elsewhere, here is what we actually use.

Servers under 4GB RAM (conservative, smaller code cache):

-Xmx<RAM> -Xms<RAM> -Djava.net.preferIPv4Stack=true
-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine
-XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:-DontCompileHugeMethods
-XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem
-XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1
-XX:AllocatePrefetchStyle=3 -XX:ReservedCodeCacheSize=256M -XX:+UseG1GC -XX:MaxGCPauseMillis=130
-XX:G1NewSizePercent=30 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1MixedGCCountTarget=3
-XX:InitiatingHeapOccupancyPercent=10 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=0
-XX:SurvivorRatio=32 -XX:MaxTenuringThreshold=1 -XX:G1SATBBufferEnqueueingThresholdPercent=30
-XX:G1ConcMarkStepDurationMillis=5

Servers 4GB RAM and above (single flexible code cache, handles modpack code patterns better):

-Xmx<RAM> -Xms<RAM> -Djava.net.preferIPv4Stack=true
-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine
-XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:-DontCompileHugeMethods
-XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem
-XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1
-XX:AllocatePrefetchStyle=3 -XX:ReservedCodeCacheSize=400M -XX:+UseG1GC -XX:MaxGCPauseMillis=130
-XX:G1NewSizePercent=28 -XX:G1HeapRegionSize=16M -XX:G1ReservePercent=20 -XX:G1MixedGCCountTarget=3
-XX:InitiatingHeapOccupancyPercent=10 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=0
-XX:SurvivorRatio=32 -XX:MaxTenuringThreshold=1 -XX:G1SATBBufferEnqueueingThresholdPercent=30
-XX:G1ConcMarkStepDurationMillis=5

Java 8 (legacy Forge, Minecraft 1.16 and earlier), a more conservative set built for the older G1 implementation:

-Xmx<RAM> -Xms<RAM> -Djava.net.preferIPv4Stack=true -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions
-XX:MaxGCPauseMillis=100 -XX:+DisableExplicitGC -XX:TargetSurvivorRatio=90 -XX:G1NewSizePercent=28
-XX:G1MaxNewSizePercent=50 -XX:G1MixedGCLiveThresholdPercent=85 -XX:+AlwaysPreTouch

Replace <RAM> with your heap size, for example 6144M for 6GB.


Doing it yourself, or letting MCHosts handle it

If you run Java yourself, getting this wrong is easy. Set -Xmx too close to the machine's total memory and you can get an out of memory kill rather than a clean Java error, with the process simply vanishing. Copy a modded flag set onto a small vanilla server and you waste memory on code cache that would be better spent on the heap. Start from the sets above, watch your TPS and GC logs, and adjust in small steps.

On MCHosts, you never write a single flag yourself. Ram Optimizations is enabled by default on every server and applies the correct tiered flag set automatically based on your plan's RAM and whether you are running a modpack. See Ram Optimizations for where to find the toggle and when you might want to turn it off for troubleshooting.


Frequently asked questions

Do these flags work on vanilla, Paper, Spigot, Forge, and Fabric? Yes. JVM flags sit below Minecraft entirely, they do not know or care which server software you are running. The RAM tiering matters more than the software choice.

Are these the same as Aikar's flags? They are related but not identical. Aikar's flags were the long standing community default and are still perfectly reasonable. The set here is a newer, independently benchmarked evolution of the same G1GC tuning approach, credited above.

Will this fix lag from a bad plugin or a chunk loading issue? No. JVM flags reduce garbage collection pauses, they cannot fix a plugin doing expensive work on the main thread, a redstone loop, or excessive chunk loading. Use a profiler like Spark to find those separately.

Can I just copy these into my own server? Yes, they are not proprietary, they come from a public benchmarking project. Just make sure -Xmx stays comfortably under your actual available memory.


Want these applied automatically, tiered correctly for your plan, with no flags to manage yourself? View plans and toggle Ram Optimizations on from day one.

Related Guides