The advice you have heard a hundred times, "give Minecraft more RAM," is half right and half wrong. Allocate too little and the game thrashes. Allocate too much and you trade frequent small pauses for rare multi-second freezes that are far worse.

The reason is the garbage collector. Minecraft on Java generates a huge amount of short-lived garbage every tick, and how the JVM cleans that garbage up matters more than the size of the heap you hand it. This is the entire reason the Aikar flag set exists.

This guide explains what those flags actually do, how to size your heap correctly, and why the popular instinct of giving Minecraft all 16 GB of your system RAM usually makes performance worse. Pinned to 1.21.4 Java Edition, client and server.

Why RAM allocation is not a performance dial

A common mental model treats RAM like fuel: more in the tank, faster you go. That is not how a managed runtime works. Minecraft only uses as much heap as its live object set needs at any moment. Extra heap beyond that does not speed up rendering or ticking. What it does is change the shape of garbage collection pauses.

The JVM splits the heap into regions. As the game allocates objects (chunk sections, entity state, particle data, network buffers), the young region fills. When it fills, the collector runs a young GC: it pauses the game briefly, finds the surviving objects, and copies them out. Most objects die young, so these pauses are short, often under a millisecond.

The problem is the old generation. Objects that survive long enough get promoted there. Eventually the old generation needs collecting too, and on a large heap that scan touches a lot of memory. The bigger the heap, the more there is to walk, and the longer the pause.

This is the counterintuitive core of the whole topic. A 16 GB heap does not pause more often than a 6 GB heap, but when it does pause for old-generation work, the pause can be several seconds instead of a fraction of one. To the player, a rare two-second freeze is far worse than frequent invisible blips.

So the goal is not "as much RAM as possible." The goal is enough headroom that the collector is not constantly busy, with a heap small enough that collection stays cheap.

What -Xmx and -Xms actually set

Two flags control the heap size:

  • -Xmx sets the maximum heap. This is the cap. Minecraft will never use more than this for the Java heap.
  • -Xms sets the initial heap. This is how much is reserved at startup.

The standard, slightly unintuitive recommendation is to set them equal:

-Xms6G -Xmx6G

The reason: if -Xms is smaller than -Xmx, the JVM grows the heap on demand, and each growth event can trigger a full GC and a pause. By starting at the maximum, you pay the memory cost once at launch and never resize again. On a dedicated server this is almost always correct. On a client where you share RAM with a browser and shaders, some players prefer a lower -Xms, but for stable tick performance, equal values win.

A note on units: 6G means 6 gibibytes. Use the capital G. -Xmx6000m is also valid if you want finer control.

How much heap to actually allocate

There is no single right number, but there is a defensible range for each setup. These are heap sizes (-Xmx), not total system RAM. Always leave the operating system, and on a server the OS plus any other services, their own memory outside the heap.

  • Vanilla or a handful of mods: 3 to 4 GB. More than this gives the collector larger old-generation scans for no benefit.
  • Mid-size modpack, 50 to 100 mods: 5 to 6 GB. This is the comfortable middle for most modded single-player and small servers.
  • Large modpack, 200-plus mods: 6 to 8 GB. Here you start to genuinely need the heap, and tuned flags matter.
  • Kitchen-sink pack, 400-plus mods, or a busy server: 8 to 12 GB, and only with properly tuned G1GC flags so the old-generation pauses stay bounded.

Going past 12 GB is rarely a fix and often a symptom. If a server needs that much, the real problem is usually entity count, chunk loading, or an unprofiled tick bottleneck. Throwing 16 or 24 GB at it tends to make the freezes longer, not shorter. Profile with Spark before you scale the heap.

A practical floor: never run a modpack on 2 GB. The collector will run almost continuously trying to keep the cramped heap clear, and you will see constant micro-stutter that looks like FPS lag but is really GC pressure.

The Aikar flags, explained line by line

The default JVM GC settings are tuned for general server workloads, not for a game that allocates aggressively and cannot tolerate long pauses. The Aikar flags are a community-standard tuning of G1GC, the Garbage-First collector, specifically for Minecraft's allocation pattern. Here is the full set:

-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

What the important ones do:

  • -XX:+UseG1GC selects the Garbage-First collector. G1 divides the heap into regions and collects them incrementally, aiming to keep pauses short and predictable instead of stopping the world for one giant sweep.
  • -XX:MaxGCPauseMillis=200 tells G1 to target pauses under 200 ms. This is a goal, not a guarantee, but it shapes how much work G1 attempts per collection.
  • -XX:G1NewSizePercent=30 and -XX:G1MaxNewSizePercent=40 force the young generation to stay large, between 30 and 40 percent of the heap. Minecraft creates mountains of short-lived objects, so a big young generation means most garbage dies there and never reaches the expensive old-generation path.
  • -XX:G1HeapRegionSize=8M sets the region size. Larger regions reduce per-region bookkeeping overhead for the heaps Minecraft typically runs.
  • -XX:InitiatingHeapOccupancyPercent=15 starts the concurrent old-generation collection cycle early, at 15 percent occupancy, so old-generation cleanup happens gradually in the background instead of all at once in a long pause.
  • -XX:MaxTenuringThreshold=1 moves surviving objects to the old generation quickly rather than copying them back and forth through survivor spaces. Combined with the large young generation, this keeps young GC cheap.
  • -XX:+AlwaysPreTouch touches every page of the heap at startup so the OS commits it immediately. Startup is a few seconds slower, but you avoid page-fault stutter during play.
  • -XX:+DisableExplicitGC ignores System.gc() calls, which some mods trigger and which would otherwise force expensive full collections at bad moments.

You do not need to memorize these. The point of the set is that the defaults bias toward throughput, while Minecraft needs short, predictable pauses. The Aikar flags trade a little raw throughput for a lot of pause consistency. That trade is almost always correct for a game.

There is a known caveat for very large heaps. On heaps above roughly 12 GB, the original G1NewSizePercent=30 can be tuned down, and Aikar's own page documents a lower value for those cases. Most players never reach that territory, so the set above is the right starting point.

Where to put the flags

On a client, open the Minecraft launcher, go to Installations, edit your installation, expand More Options, and replace the contents of JVM Arguments. Keep your -Xmx and -Xms at the front, then paste the flag block. Remove the launcher's default -XX:+UnlockExperimentalVMOptions line if it duplicates one in the set.

On a server, the flags go into your start script between java and -jar:

java -Xms6G -Xmx6G -XX:+UseG1GC -XX:+ParallelRefProcEnabled \
  -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions \
  -XX:+DisableExplicitGC -jar server.jar nogui

Hosting panels such as Pterodactyl usually expose a JVM-arguments or additional-flags field; paste the set there rather than editing the jar invocation by hand.

Verify the flags are working

Do not assume. Confirm. The simplest check is to print the GC log and watch pause times. Add this temporarily:

-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=10M

Play for a few minutes, then open gc.log. You want to see frequent young pauses well under your 200 ms target and no full GC entries during normal play. If you see repeated multi-second pauses, your heap is probably too large for the work, or a mod is leaking and forcing old-generation pressure.

A faster sanity check during play: press F3 on the client and watch the memory bar, or run Spark on a server and check the GC statistics. A healthy tuned setup shows the heap sawtoothing comfortably below the cap with short, regular collections, not a flat line creeping toward 100 percent before a freeze.

The one-paragraph version

Set -Xms and -Xmx equal. Pick a heap in the 4 to 8 GB range for most setups and only go higher if profiling proves you need it. Add the Aikar G1GC flag set so pauses stay short and predictable. Then stop touching RAM and go profile the actual tick bottleneck, because past a sane heap size, the garbage collector is no longer your problem.


Sources & further reading: