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,
Step by Step:
0. Install the JDK11 or higher on your setup
0.5. Verify that the new JDK is detected and being used
java -versionin your terminal, powershell or cmd. You should get something like this:
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):
(If anyone else is prepared to log G1 GC on a bigger server please contact me.)
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
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. Also see point 3.
3. Monitor ZGC
ZGC has some issues with servers that as leaking or just allocating memory too quickly (Survival servers for example).
To see if your ZGC is performing well, you can monitor the servers memory usage using JMX or logging GC to a file.
To log GC to a file add the following flag:
-Xlog:gc*:logs/gc.log:time,uptime:filecount=2,filesize=8M, this will add a file called gc.log in your logs directory.
After running the server for at least an hour at normal load, open the file and search for the following text
Allocation Stall (. If you can’t find a single line matching that text, look at the last occurrence of the following text:
Critical: Allocation Stall. If it looks something like this:
Critical: Allocation Stall 0.000 / 0.000 0.000 / 0.000 0.000 / 0.000 0.000 / 0.000 ms
then your ZGC should be running fine without issues.
If you see a bunch of lines showing allocations stalls (example below) and a high number in the summary, you should try an investigate the issue, allocate more memory or switch back to G1.
[2020-04-13T00:11:41.490+0200][682.338s] Allocation Stall (Craft Scheduler Thread - 46) 290.661ms [2020-04-13T00:11:41.490+0200][682.338s] Allocation Stall (Server thread) 269.893ms [2020-04-13T00:11:41.490+0200][682.338s] Allocation Stall (mysql-cj-abandoned-connection-cleanup) 0.207ms Critical: Allocation Stall 0.000 / 0.000 223.572 / 458.018 200.367 / 458.018 200.367 / 458.018 ms
How the same information can be seen on JMX graphs:
On the start of the graph, the committed memory is close to the max, but never reaches it, this in most cases indicates that ZGC has enough room to clean without stalls.
After the middle you can see the committed memory hit the max and stay there, this will result in stalls resulting in lag. You can try to fix this issue by adding more memory, but this might not work in all cases as shown below:
In this case I recommend switching back to G1GC, as it’s better at handling memory leaks and high allocation speeds.
Some example of well running ZGC setups:
As you can see there is plenty of ram leftover. In a case like this you can lower the max ram to save some memory or leave some headroom if you like flexing with memory.
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?