Minecraft-Performance-Flags-Benchmarks
Minecraft-Performance-Flags-Benchmarks copied to clipboard
Sane, Benchmarked Java Flags and Tweaks for Minecraft
Benchmarks
Flags are tested with Benchmark.py script. See the work-in-progress Benchmarks.md.
Discord: https://discord.gg/zeFSR9PnUw
Base Java Flags
These optimized flags will work with any Java 11+ build, though the newest version of Java you can get is recommended:
-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:-DontCompileHugeMethods -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:+OmitStackTraceInFastThrow -XX:ThreadPriorityPolicy=1
They work on both servers and clients, but you need to add some garbage collection flags from below!
All OpenJDK distributions like Eclipse Adoptium, Microsoft, Azul, Amazon Correto and so on perform almost identically, with 2 major exceptions I know of: Oracle's GraalVM, and Intel's Clear Linux OpenJDK.
Memory Allocation
Minimum and maximum (-xms
and -xmx
) values should be set to the same value, as explained here: https://dzone.com/articles/benefits-of-setting-initial-and-maximum-memory-siz
Allocating too much memory can force your operating system to page, make garbage collection pauses more severe, or slow the game down. Allocating too little can also slow the game down. Less than 8GB is usually sufficient, but experiment with your setup.
Garbage Collection
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. Pick one set from below:
ZGC
ZGC is great for high memory/high core count servers. It has no throughput hit I can measure, and absolutely does not stutter like G1GC. However, it requires more RAM and more cores than other garbage collectors.
Unfortunately, it has a significant client FPS hit on my (8-core) laptop. See the "ZGC" benchmark in the benchmarks folder.
-XX:+UseZGC
enables it, but allocate more RAM and more ConcGCThreads
than you normally would for other GC. Additional flags are still being investigated, but try -XX:-ZProactive
, as we don't really care about reclaiming memory when idle.
Shenandoah
Shenandoah performs well on clients, but kills server throughput in my tests. Enable it with -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:-ShenandoahUncommit
See more tuning options here. The "herustic" and "mode" options don't change much for me (except for "compact," which you should not use).
If you are a Java 8 user, Red Hat builds Java 8 with Shenandoah: https://access.redhat.com/products/openjdk
G1GC
Client:
G1GC is the default garbage collector, and is the only available garbage collector for GraalVM users. Aikar's famous Minecraft G1GC arguments run great on clients, with one caveat: they effectively clamp the MaxGCPauseMillis
parameter, producing long stutters on some systems.
These are similar to the aikar flags, but with shorter, more frequent pauses and less aggressive G1 mixed collection and some tweaks to background collection: -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 -XX:G1ConcRSHotCardLimit=16 -XX:G1ConcRefinementServiceIntervalMillis=150 -XX:GCTimeRatio=99 -XX:AllocatePrefetchStyle=3
I recommend using thesparkc gcmonitor
command in the Spark mod to observe G1GC pauses yourself. Any old generation pauses are bad, and young generation collections should be infrequent, but short enough to be imperceptible.
G1NewSizePercent
and MaxGCPauseMillis
can be used to tune the frequency of 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.
Server:
Longer pauses are more acceptable on servers. These flags are very close to the aikar defaults:
-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:AllocatePrefetchStyle=3
Multicore Garbage collection.
-XX:ConcGCThreads=[Some Number]
controls the maximum number of background threads the garbage collector is allowed to use, and defaults to hyperthreaded cores / 4
.
In some cases (especially with ZGC or Shenandoh) you want to increase this number. I recommend ~number of real cores - 1
.
Large Pages
Enabling large pages improves the performance of Minecraft servers and clients. Here are some great tutorial for enabling it on Windows:
https://www.chaoticafractals.com/manual/getting-started/enabling-large-page-support-windows https://kstefanj.github.io/2021/05/19/large-pages-and-java.html
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
. But if you want to manually allocate some server memory for Minecraft for even better performance, 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
GraalVM Enterprise Edition
GraalVM is a new high performance Java VM from Oracle that can improve the performance of (modded) Minecraft. While client FPS gains are modest, server-side workloads like chunk generation can get a 20%+ boost!
Unfortunately, only GraalVM Enterprise Edition comes with the full set of optimizations, and downloading it requires making a free Oracle account.
Register and download it here: https://www.oracle.com/downloads/graalvm-downloads.html
Grab the newest "Oracle GraalVM Enterprise Edition Core" release available for Java 17+ (or Java 11 if you need it) from the "Archives" section. Unzip it, and put the unzipped folder somewhere safe.
Again, you must use 22.1.0, not 22.2.0.
These releases are not Java installers. You need to manually replace your launcher's version of Java, or use a Minecraft launcher that supports specifying your Java path. I recommend PolyMC, ATLauncher, or GDLauncher. When specifying a java path, navigate to the "bin" folder in the GraalVM download and use "javaw.exe" or "java.exe"
For servers, you need to replace the "java" command in your server start sh/bat file with the full path to graalvm java, in quotes.
If you don't feel comfortable making an Oracle account, grab the latest GraalVM CE release and use the flags from above ^. But Oracle does not check the information you put into the registration page, and GraalVM CE lacks most of the EE optimizations.
GraalVM EE Java Arguments
General arguments for GraalVM EE 22+ Java 17 (or Java 11):
-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:-DontCompileHugeMethods -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:+OmitStackTraceInFastThrow -XX:+EnableJVMCIProduct -XX:+UseJVMCICompiler -XX:+EagerJVMCI -Dgraal.TuneInlinerExploration=1 -Dgraal.CompilerConfiguration=enterprise -Dgraal.UsePriorityInlining=true -Dgraal.Vectorization=true -Dgraal.OptDuplication=true -Dgraal.DetectInvertedLoopsAsCounted=true -Dgraal.LoopInversion=true -Dgraal.VectorizeHashes=true -Dgraal.EnterprisePartialUnroll=true -Dgraal.VectorizeSIMD=true -Dgraal.StripMineNonCountedLoops=true -Dgraal.SpeculativeGuardMovement=true -Dgraal.InfeasiblePathCorrelation=true -Dgraal.LoopRotation=true
Many of the Dgraal
arguments are redundant/default, but are there for easy testing. Again, you must use G1GC as your garbage collector when running GraalVM CE or EE.
GraalVM EE Mod Compatibility
So far, I have documented a few rendering bugs related to GraalVM EE:
-
GraalVM 22.2.0 has issues with Minecraft, particularly with the
UsePriorityInlining
flag enabled. Please use 22.1.0 until 22.3.0 is out. See: https://github.com/oracle/graal/issues/4776 -
VectorizeSIMD
turns entities invisible with Iris or Occulus... but only under certain conditions. This is a tricky bug I am still tracking down, but for now you can work around it by disablingVectorizeSIMD
. See: https://github.com/oracle/graal/issues/4849 -
GraalVM CE and EE both break constellation rendering in Astral Sorcery. See: https://github.com/HellFirePvP/AstralSorcery/issues/1963
If you run into any other mod issues you can trace back to GraalVM, please create a Github issue or post in the Discord! Generally, you can work around them by disabling the dgraal
flags one at a time, or by finding the right function with Dgraal.PrintCompilation=true
, and working around it with -Dgraal.GraalCompileOnly=~...
once you find the miscompiled function.
SpecialK
SpecialK has 2 major Windows performance benefits:
-
A frame limiter that reduces stutter, eliminates tearing, saves power, and saves CPU TDP to boost when needed. It even works in conjuction with VRR or Nvidia Reflex.
-
A OpenGL-to-DirectX11 wrapper called OpenGL-IK that eliminates Minecraft's windowed moded overhead, and enables other features (like HDR).
Download it here: https://wiki.special-k.info/en/SpecialK/Tools
Add your MC launcher, and check the "elevated service" checkbox. Then navigate to your java bin folder where your javaw.exe is, and create an empty file called SpecialK.OpenGL32
. Launch your Minecraft launcher with the SpecialK launcher, and the launcher will then "inject" SpecialK into Minecraft.
You can create a desktop shortcut to your Minecraft launcher through the SpecialK UI for even more convenience.
Be sure to turn off VSync and the in-game Minecraft frame limiter.
Process Priority
After launching Minecraft, set Java to run at a high process priority in Windows with the task manager:
Linux users can append the command sudo nice -n -18
to thier launch arguments.
Performance Mods
This is a fantastic repo for finding performance mods: https://github.com/NordicGamerFE/usefulmods
Other Performance Notes
-
Make sure Minecraft is using your discrete GPU! Check the F3 tab, and force Minecraft to use it in the "Windows Graphics Settings", not the AMD/Nvidia control panel (as they don't seem to work anymore).
-
Minecraft client linux users should check out https://github.com/Admicos/minecraft-wayland
-
Host Minecraft servers (and clients!) on Clear Linux over any other Linux distro. In spite of the name, it works great on Intel and AMD CPUs/GPUs... just not Nvidia GPUs: https://docs.01.org/clearlinux/latest/tutorials/multi-boot/dual-boot-win.html
-
Close everything in the background, including Discord and your browser! Minecraft is resource intensive, and does not like other apps generating CPU interrupts or eating disk I/O, RAM and so on.
-
IBM's OpenJ9 is over 30% slower at server chunkgen in my tests. Tweaking it is a low priority on my todo list, If there are any flags that make it competitive with OpenJDK, please let me know: https://github.com/brucethemoose/Minecraft-Performance-Flags-Benchmarks/issues/9
Java 8
Java 8 has not been tested as much as 17, as I tend to play 1.18.2. But these flags will work with OpenJDK8, along with Shenandoh GC (for Red Hat OpenJDK on clients) or G1GC (for everything else):
-server -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch -Dsun.rmi.dgc.server.gcInterval=2147483646 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -XX:+UseFastUnorderedTimeStamps -XX:+UseAES -XX:+UseAESIntrinsics -XX:AllocatePrefetchStyle=1 -XX:+UseLoopPredicate -XX:+RangeCheckElimination -XX:+EliminateLocks -XX:+DoEscapeAnalysis -XX:+UseCodeCacheFlushing -XX:+UseFastJNIAccessors -XX:+OptimizeStringConcat -XX:+UseCompressedOops -XX:+UseThreadPriorities -XX:+OmitStackTraceInFastThrow -XX:ThreadPriorityPolicy=1 -XX:+UseInlineCaches -XX:+RewriteBytecodes -XX:+RewriteFrequentPairs -XX:+UseNUMA -XX:-DontCompileHugeMethods -XX:+UseFPUForSpilling -Dgraal.CompilerConfiguration=community -Dgraal.SpeculativeGuardMovement=true
x86 Java 8 users (aka most Java 8 users) can add these additional arguments:
-XX:+UseNewLongLShift -XX:+UseXMMForArrayCopy -XX:+UseXmmI2D -XX:+UseXmmI2F -XX:+UseXmmLoadAndClearUpper -XX:+UseXmmRegToRegMoveAll -XX:+UseNewLongLShift
You can get Java 8 versions of GraalVM EE from the 21.X release section on the Oracle site, and use these arguments:
-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch -XX:+EnableJVMCI -XX:+UseJVMCICompiler -XX:+EagerJVMCI -XX:+UseFastUnorderedTimeStamps -XX:AllocatePrefetchStyle=1 -XX:+UseLoopPredicate -XX:+RangeCheckElimination -XX:+EliminateLocks -XX:+DoEscapeAnalysis -XX:+UseCodeCacheFlushing -XX:+UseFastJNIAccessors -XX:+OptimizeStringConcat -XX:+UseCompressedOops -XX:+UseThreadPriorities -XX:+OmitStackTraceInFastThrow -XX:ThreadPriorityPolicy=1 -XX:+UseInlineCaches -XX:+RewriteBytecodes -XX:+RewriteFrequentPairs -XX:+UseNUMA -XX:-DontCompileHugeMethods -XX:+UseFPUForSpilling -Dgraal.TuneInlinerExploration=1 -Dgraal.CompilerConfiguration=enterprise -Dgraal.UsePriorityInlining=true -Dgraal.Vectorization=true -Dgraal.OptDuplication=true -Dgraal.DetectInvertedLoopsAsCounted=true -Dgraal.LoopInversion=true -Dgraal.VectorizeHashes=true -Dgraal.EnterprisePartialUnroll=true -Dgraal.VectorizeSIMD=true -Dgraal.StripMineNonCountedLoops=true -Dgraal.SpeculativeGuardMovement=true -Dgraal.InfeasiblePathCorrelation=true
Flag Explanations
- Aikar G1GC flags are explained here: https://aikar.co/2018/07/02/tuning-the-jvm-g1gc-garbage-collector-flags-for-minecraft/
-
-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions
simply unlock more flags to be used. These can be listed with the-XX:+PrintFlagsFinal
and-XX:+JVMCIPrintProperties
flags, see Flag Dumps -
-XX:+UseNUMA
enables optimizations for multisocket systems, if applicable. Not sure if this applies to MCM CPUs like Ryzen or Epyc. -
-XX:-DontCompileHugeMethods
Allows huge methods to be compiled. Modded Minecraft has some of these, and we don't care about higher background compiler CPU usage. - ~~
-XX:+UseStringDeduplication
~~ This is a popular option, but not used here, as it's benching slower. -
-XX:+UseFastUnorderedTimeStamps
Avoid system calls for getting the time. The impact of this will vary per system, but we aren't really concerned with logging timestamp accuracy. -
-XX:+UseCriticalJavaThreadPriority
Nothing should preempt the Minecraft threads. GC and Compiler threads can wait. -
-XX:+OmitStackTraceInFastThrow
Minecraft throws a ton of "safe" errors we don't want to spend CPU tracing. -
-XX:ThreadPriorityPolicy=1
Use a wider range of thread priorities. Requires sudo on linux to work. Some JDKs (like Graal) enable this by default, but some don't. -
-XX:G1SATBBufferEnqueueingThresholdPercent=30 -XX:G1ConcMarkStepDurationMillis=5 -XX:G1ConcRSHotCardLimit=16 -XX:G1ConcRefinementServiceIntervalMillis=150
: Optimizes G1GC's concurrent collection, still being tested: https://research.spec.org/icpe_proceedings/2014/p111.pdf -
-XX:G1RSetUpdatingPauseTimePercent=0
: We want all this work to be done in the G1GC concurrent threads, not the pauses. -
-XX:G1HeapWastePercent=18
Don't bother collecting from old gen until its above this percent. This avoids triggering slower "mixed" young generation GCs, which is fine since Minecraft (with sufficient memory) doesn't fill the old gen that fast. Idea from: https://www.reddit.com/r/Minecraft/comments/k9zb7m/tuning_jvm_gc_for_singleplayer/ -
-XX:GCTimeRatio=99
As a goal, 1% of CPU time should be spent on garbage collection. Default is 12, which seems way too low. -
-XX:AllocatePrefetchStyle=3
Generate one prefetch instruction per cache line instead of per allocation. It seems to break ZGC, hence its only enabled for G1GC. -
-Dgraal.LoopRotation=true
A non default optimization, will probably be default soon. -
-Dgraal.TuneInlinerExploration=1
Spend more time making inlining decisions. For Minecraft, we want the C2 compiler to be as slow and aggressive as possible. - Most other
-Dgraal
arguments are enabled by default, and are either there as a sanity check, for debugging or as a failsafe (if, for instance, someone unknowingly disables JVCMI with some other flag).
Flags Under Consideration:
- Increased code cache, but only if profiling with
-XX:+PrintCodeCache
suggests modded Minecraft fills up the code cache: https://docs.oracle.com/javase/8/embedded/develop-apps-platforms/codecache.htm - More aggressive inlining, via
-Dgraal.BaseTargetSpending=160
(default 120) in Graal and some other flags in OpenJDK. - OpenJDK flags which are disabled by default:
-XX:+AlignVector -XX:+OptoBundling -XX:+OptimizeFill -XX:+AlwaysCompileLoopMethods -XX:+EnableVectorAggressiveReboxing -XX:+EnableVectorSupport -XX:+OptoScheduling -XX:+UseCharacterCompareIntrinsics -XX:+UseCopySignIntrinsic -XX:+UseVectorStubs
- ~~
-Dgraal.LSRAOptimization=true
~~ seems to hurt performance -
-Dgraal.OptWriteMotion=true
andgraal.WriteableCodeCache=true
, which do not seem stable, but may be more stable in GraalVM 22.3.0 - Extreme
G1HeapWastePercent
values.
Sources
- Updated Aikar flags from this repo: https://github.com/etil2jz/etil-minecraft-flags
- Reddit post from a Forge dev: https://www.reddit.com/r/feedthebeast/comments/5jhuk9/modded_mc_and_memory_usage_a_history_with_a/
- Red Hat's optimization guide: https://www.redhat.com/en/blog/optimizing-rhel-8-run-java-implementation-minecraft-server
- GraalVM release notes: https://www.graalvm.org/release-notes/
- Oracle's Java 17 Documentation: https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html
- VM Options explorer: https://chriswhocodes.com/
- Java itself, via the
-XX:+PrintFlagsFinal
and the-XX:+JVMCIPrintProperties
flags to dump flag descriptions/defaults. - OpenJDK source: https://github.com/openjdk/jdk/
- Testing from @keyboard.tn in Discord.
- https://research.spec.org/icpe_proceedings/2014/p111.pdf
- https://www.diva-portal.org/smash/get/diva2:1466940/FULLTEXT01.pdf
- https://malloc.se/blog/zgc-jdk17