This is a guide to tune Java for Minecraft. Every flag and tweak is individually benchmarked to test for regressions, and checked against Java defaults to avoid redundancy.
While these tweaks notably reduce some server and client stutters, expect only modest TPS gains + minimal FPS gains at best, and somewhat increased RAM + CPU usage. And they are no substitute for clearing laggy things out with mods like Spark or Observable.
Discord for questions and such: https://discord.gg/zeFSR9PnUw
All flags are tested with Benchmark.py script. See the work-in-progress Benchmarks.md.
For Minecraft 1.16.5 and up, use Java 17. Some launchers like Curseforge and Prism Launcher ask you to use Java 8 on 1.16.X, but Minecraft 1.16.5+, all 1.18+ mods, and most 1.16.5 mods are compatible with Java 17.
Sometimes Java 11 will work where Java 17 doesn't.
1.12.2 and below generally requires Java 8.
Java runtimes from Azul, Microsoft, Adoptium, Amazon and so on are basically identical. Some notable exceptions:
Oracle GraalVM Enterprise Edition features a more aggressive Java compiler. This is what I personally run Minecraft with, see the GraalVM section below.
Intel's Clear Linux OpenJDK uses the same code as any other OpenJDK (making it highly compatible), but the build process itself and the dependencies are optimized for newer CPUs. Grab it from Clear Linux's repos via swupd, from Distrobox, or from Docker. Note that you must roll back to the Java 17 release, and that Java 18 reverts some of the performance enhancements.
Azul's Prime OpenJDK is very fast since it hooks into llvm, but its currently incompatible with most mods and is linux-only. Get it from here: https://docs.azul.com/prime/prime-quick-start-tar
Red Hat Java 8 has the Shenandoah garbage collector. Its gated behind a free email signup: https://developers.redhat.com/products/openjdk/download
IBM's OpenJ9 is... much slower in Minecraft, and uses totally different flags than any other Java build, but it does consume less memory than OpenJDK-based runtimes. See FAQ, the Benchmarks folder, and this Gist for low memory consumption flags.
If you dont know what to pick, I recommend GraalVM EE (see below) or the latest Adoptium Java 17 JRE: https://adoptium.net/
Couleur maintains a good running list of JREs here: https://rentry.co/JREs
These optimized flags run with any Java 11+ build. They work on both servers and clients:
-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1 -XX:AllocatePrefetchStyle=3
You must add garbage collection flags to these java arguments.
A full set of flags looks like this:
-Xmx8G -Xms8G -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1 -XX:AllocatePrefetchStyle=3 -XX:+UseG1GC -XX:MaxGCPauseMillis=37 -XX:+PerfDisableSharedMem -XX:G1HeapRegionSize=16M -XX:G1NewSizePercent=23 -XX:G1ReservePercent=20 -XX:SurvivorRatio=32 -XX:G1MixedGCCountTarget=3 -XX:G1HeapWastePercent=20 -XX:InitiatingHeapOccupancyPercent=10 -XX:G1RSetUpdatingPauseTimePercent=0 -XX:MaxTenuringThreshold=1 -XX:G1SATBBufferEnqueueingThresholdPercent=30 -XX:G1ConcMarkStepDurationMillis=5.0 -XX:G1ConcRSHotCardLimit=16 -XX:G1ConcRefinementServiceIntervalMillis=150 -XX:GCTimeRatio=99 -XX:+UseLargePages -XX:LargePageSizeInBytes=2m
Minimum and maximum (-xms and -xmx) memory should be set to the same value, as explained here: https://dzone.com/articles/benefits-of-setting-initial-and-maximum-memory-siz
One exception: if you are on a low-memory system, and Minecraft takes up almost all your RAM, set your minimum memory below your maximum memory to conserve as much as possible.
Sizes are set in megabytes (-Xms4096M) or gigabytes (-Xmx8G)
Allocating too much memory can break gc or just slow Minecraft down, even if you have plenty to spare. Allocating too little can also slow down or break the game. Keep a close eye on the Windows Task manager (or your DE's system monitor) as Minecraft is running, and allocate only as much as it needs (which is usually less than 8G). sparkc gcmonitor will tell you if your allocation is too high (the pauses will be too long) or too low (frequent GC with a low memory warning in the notification).
Garbage collection flags must be added to Minecraft servers and clients, as the default "pauses" to stop and collect garbage manifest as stutters on the client and lag on servers. Use the /sparkc gcmonitor command in the Spark mod to observe pauses in-game. Any old generation pauses are bad, and young generation G1GC collections should be infrequent, but short enough to be imperceptible.
Pick one set of flags. I recommend Shenandoah on clients, ZGC on powerful Java 17 servers, and G1GC on Graal or on servers/clients with less RAM and fewer cores:
ZGC is great for high memory/high core count servers. It has no server throughput hit I can measure, and absolutely does not stutter. However, it requires more RAM and more cores than other garbage collectors.
Unfortunately, it has a significant client FPS hit on my (8-core/16 thread) laptop. See the "ZGC" benchmark in the benchmarks folder. Its not available in Java 8, and much less performant in Java 11 than in Java 17.
-XX:+UseZGC -XX:AllocatePrefetchStyle=1 -XX:-ZProactive enables it, but allocate more RAM and more ConcGCThreads than you normally would for other GC. Note that ZGC does not like AllocatePrefetchStyle=3, hence setting it to 1 overrides the previous entry.
U
Shenandoah performs well on clients, but kills server throughput in my tests. Enable it with -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:AllocatePrefetchStyle=1
See more tuning options here. The "herustic" and "mode" options don't change much for me (except for "compact," which you should not use). Like ZGC, Shenandoah does not like AllocatePrefetchStyle=3.
Note that Shenandoah is not in Java 8. Its also not in any Oracle Java builds! If you are a Java 8 user, you must use Red Hat OpenJDK to use Shenandoah: https://developers.redhat.com/products/openjdk/download
G1GC is the default garbage collector, and is the only available garbage collector for GraalVM users. Aikar's famous Minecraft server G1GC arguments run great on clients, with two caveats: they effectively clamp the MaxGCPauseMillis parameter by setting G1NewSizePercent so high, producing long stutters on some clients, and they collect oldgen garbage too aggressively (as the client produces far less than a populated server).
These are similar to the aikar flags, but with shorter, more frequent pauses, less aggressive G1 mixed collection and more aggressive background collection: -XX:+UseG1GC -XX:MaxGCPauseMillis=37 -XX:+PerfDisableSharedMem -XX:G1HeapRegionSize=16M -XX:G1NewSizePercent=23 -XX:G1ReservePercent=20 -XX:SurvivorRatio=32 -XX:G1MixedGCCountTarget=3 -XX:G1HeapWastePercent=20 -XX:InitiatingHeapOccupancyPercent=10 -XX:G1RSetUpdatingPauseTimePercent=0 -XX:MaxTenuringThreshold=1 -XX:G1SATBBufferEnqueueingThresholdPercent=30 -XX:G1ConcMarkStepDurationMillis=5.0 -XX:G1ConcRSHotCardLimit=16 -XX:G1ConcRefinementServiceIntervalMillis=150 -XX:GCTimeRatio=99
G1NewSizePercent and MaxGCPauseMillis can be used to tune the frequency/dureation of your young generation collections. G1HeapWastePercent=18 should be removed if you are getting any old generation pauses on your setup. Alternatively, you can raise it and set G1MixedGCCountTarget to 2 or 1 to make mixed garbage collection even lazier (at the cost of higher memory usage).
Longer pauses are more acceptable on servers. These flags are very close to the aikar defaults:
-XX:+UseG1GC -XX:MaxGCPauseMillis=130 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -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 -XX:G1ConcRSHotCardLimit=16 -XX:G1ConcRefinementServiceIntervalMillis=150
-XX:ConcGCThreads=[Some Number] controls the maximum number of background threads the garbage collector is allowed to use, and defaults to logical (hyperthreaded) cores / 4. Recent versions of Java will reduce the number of gc threads, if needed.
In some cases (especially with ZGC or Shenandoh) you want to increase this thread cap past the default. I recommend "2" on CPUs with 4 threads, and [number of real cores - 2] on most other CPUs, but you may need to play with this parameter. If its too low, garbage collection can't keep up with Minecraft, and the game will stutter and/or start eating gobs of RAM and crash. If its too high, it might slow the game down, especially if you are running Java 8.
No other "threading" flags like ParallelGCThreads or JVMCIThreads are necessary, as they are enabled by default with good automatic settings in Java 8+.
NOTE: Large Pages requires admin privledges on Windows. This is a security risk, and you should skip this section if you aren't comfortable with that.
Enabling large pages improves the performance of Minecraft servers and clients. Here are some great tutorials for enabling it:
On Windows, you must run java, and your launcher, as an administrator. That means checking the "run as administrator" compatibility checkbox for javaw.exe, java.exe and your launcher.exe, otherwise Large Pages will silently fail. Add -XX:+UseLargePages -XX:LargePageSizeInBytes=2m to your arguments.
On linux, you generally want to use -XX:+UseTransparentHugePages. To manually allocate memory instead (for a bigger performance boost), Red Hat has a good tutorial for RHEL-like linux distros, like Fedora, CentOS, or Oracle Linux: https://www.redhat.com/en/blog/optimizing-rhel-8-run-java-implementation-minecraft-server
Check and see if large pages is working with the -Xlog:gc+init java argument in Java 17.
In any Java version/platform, if large pages isn't working, you will get a warning in the log similar to this:
Java HotSpot(TM) 64-Bit Server VM warning: JVM cannot use large page memory because it does not have enough privilege to lock pages in memory.
GraalVM is a new Java VM from Oracle that can improve the performance of (modded and vanilla) Minecraft. While client FPS gains are modest, server-side workloads like chunk generation can get a 20%+ boost!
Only GraalVM Enterprise Edition comes with the full set of optimizations. Download it via direct links from Oracle:
Java 17
Windows AMD64 (64-bit): https://oca.opensource.oracle.com/gds/GRAALVM_EE_JAVA17_22_3_1/graalvm-ee-java17-windows-amd64-22.3.1.zip
Linux AMD64 (64-bit): https://oca.opensource.oracle.com/gds/GRAALVM_EE_JAVA17_22_3_1/graalvm-ee-java17-linux-amd64-22.3.1.tar.gz
Linux AARCH64 (ARM 64-bit): https://oca.opensource.oracle.com/gds/GRAALVM_EE_JAVA17_22_3_1/graalvm-ee-java17-linux-aarch64-22.3.1.tar.gz
Mac AMD64 (64-bit): https://oca.opensource.o
$ claude mcp add Minecraft-Performance-Flags-Benchmarks \
-- python -m otcore.mcp_server <graph>