Higher performance crafting: Using JDK11+ and ZGC.

Still using JDK8 and G1 GC? Upgrade!


Disclaimer: I am in no way, shape, or form responsible for your actions. If you burn down your dirt house or hurt your feelings, don’t blame me. But anyway, happy tweaking.


What do you need?

    • JDK13+ (JDK11 is not recommended due to a slow ZGC implementation!) on Linux or JDK14+ on Mac and Windows,
    • Install privileges or just run a portable version of the JDK,
    • Sheep.

Step by Step:

  • 0. Install the JDK11 or higher on your setup

On almost any platform, AdoptJDK will get you up to date with the latest JDK, just follow their instructions (scroll down to the installer section): https://adoptopenjdk.net/installation.html
  • 0.5. Verify that the new JDK is detected and being used

Run  java -version in your terminal, powershell or cmd. You should get something like this: 2020-03-25_15-16-12
If you don’t get the correct version information, make sure to remove old versions from your setup and set the correct JAVA_HOME path.
  • 1. Why should I use ZGC instead of G1 GC?

Simple, ZGC is WAY faster, like super fast. Here are some graphs that show garbage collection time on a MC server with 8GB of allocated ram and ~60 players online at peak time:
Testing was done on an Intel 9900k (non OC), HT enabled, 4x8GB 2133Mhz memory, 2x 1TB Crucial NVMe drives, transparent huge pages enabled and running on Java 13.


Garbage collection time scaled to 1 ms:

And the same graph scaled to 50 ms (1 Minecraft tick):


G1 GC with flags from my startup script on a server that has 1-3 players online at peek.
(If anyone else is prepared to log G1 GC on a bigger server please contact me.)
Graph scaled to 1 ms:

And the same graph scaled to 50 ms (1 Minecraft tick):

The GC time with ZGC is negligible, on the other hand G1 with the suggested flags tries to stay below 100 ms, whereas ZGC was designed to stay below 10 ms, at any memory range, without thread blocking.
  • 2. Enable ZGC

Enabling ZGC depends on your startup method (some flags are not required, but are added to avoid issues):
Linux bash script: You can just use my script from here.
Mark2: Add the following line to your mark2.properties:  java.cli_extra=-XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:-UseParallelGC -XX:-UseParallelOldGC -XX:-UseG1GC -XX:+UseZGC
Anywhere else: Just replace your flags with:  -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:-UseParallelGC -XX:-UseParallelOldGC -XX:-UseG1GC -XX:+UseZGC
Quick explanation of the flags:
+UnlockExperimentalVMOptions – Unlocks experimental flags/options,
+DisableExplicitGC – Disables System.gc() calls from code, you really don’t want people playing around with your GC,
-UseParallelGC – Disables Parallel GC, this should already be disabled, but we set this just to be sure,
-UseParallelOldGC – ^ but disables ParalledOld GC,
-UseG1GC – ^ but disables G1 GC,
+UseZGC – Enables ZGC.

*If you notice degraded performance, higher CPU usage, more memory commits/uncommits, setting the following flag might help you -XX:-ZUncommit.
That will tell ZGC not free up unused memory. This can help on shared systems or system with low memory bandwidth, do note the flag only being added in JDK13.
IF you still want uncommits, you can try using Large Pages/Transparent Huge Pages.


That’s It, You’re Done!

No tuning needed, it just works. There are some things you could tune, but AdoptJDK already does that for you.
To verify that you are actually using ZGC you can use Timings v2 (that are build into Paper).

Don’t use plugins to check GC time, as they are not yet optimized for ZGC (Spark for example), use something like Java Management Extensions (JMX) or Timings V2 (Thanks to Aikar for adding a GC option).

If you have plugin issues due to Java being updated, yell to the plugin authors! There are 7-year-old plugins that still run on JDK 14 and Minecraft 1.15.2, while they can’t create a plugin that works on JDK9+ in 2020?