Native Minecraft Servers with GraalVM Native Image

Jonathan Grenda
graalvm
Published in
7 min readSep 1, 2022
Bringing the benefits of GraalVM Native Image to Minecraft.

This is a guest blog post by Jonathan Grenda, a master’s student from the Software Architecture Group in the Hasso Plattner Institute at the University of Potsdam, Germany.

Outline

  1. Motivation
  2. AOT-Compiling the Minecraft Server
  3. How to Build a Native Minecraft Server
  4. Preliminary Results
  5. Outlook

Motivation

Minecraft is the #1 most sold video game around the world. One could argue that especially the multiplayer aspects helped it become the cultural phenomenon that it is today. Surviving, exploring, playing mini-games, or building is just that much more enjoyable with friends. Since 2009, Minecraft’s server.jar has been available for everyone who wants to host a multiplayer server. But while people might have been technically able to do so, some people’s computers just couldn’t handle the steep resource requirements in terms of CPU or memory that these servers required even for just a couple of players. While memory and CPU resources are much more affordable compared to 13 years ago, the performance of Minecraft servers has continuously left server admins wishing for better alternatives.

Over the course of a summer semester at the Hasso Plattner Institute (HPI) a student project looked into this exact issue. The goal was to create a working, native executable Minecraft server leveraging the benefits that native deployments grant compared to JVM executions. But just how much better can such a server be?

AOT-Compiling the Minecraft Server

The Minecraft server is a large Java application that typically runs on the JVM. The JVM provides a platform-independent runtime, garbage collected memory management, and just-in-time (JIT) native machine code compilation. Developers have to rely on the dynamic optimizations that the JIT compiler provides, and this brings its own suite of drawbacks when looking at the startup time or the resource footprint of applications.

GraalVM Native Image provides a different approach, performing extensive static analysis at build-time to remove the need for a JIT compiler at run-time. This is similar to the ahead-of-time compilation approach employed by Rust, C++, Go, among others. For this, a set of assumptions are made that allow the analysis to encompass everything required to build a native executable. The primary assumption here is the closed-world assumption (CWA), meaning that everything executed at run-time must be available and visible for the static analysis at build-time. Dynamically including additional code at run-time is not allowed as this would break the CWA. Enforcing the CWA grants GraalVM Native Image the opportunity for aggressive optimization (for example through devirtualization), removing any classes, methods, or fields from the application and contents of the JVM environment that are unused according to the analysis performed beforehand.

GraalVM Native Image Build Process (source)

The primary challenge for this project was the use of Java reflection within the Minecraft server. In Java, it is possible to access classes, methods, or fields dynamically at run-time. This, however, is hard to detect automatically by any static analysis at build-time. Unfavorably, Minecraft heavily uses reflection and you won’t even be able to launch the server without supporting it properly. For this reason, GraalVM Native Image supports the inclusion of configuration files that specify for example what parts of the program are accessed through reflection and should therefore be included and linked in the final product even if the static analysis would not do so under regular circumstances. While these files can be created by hand, the recommended approach to get started (especially for larger applications) is to use the Native Image Tracing Agent. This agent looks at a regular JVM execution of the desired program and traces reflective accesses and other dynamic features of Java, recording them in the desired configuration file format. This guarantees every reflective call made during the JVM execution is also possible in the generated native executable.

But as you may have wondered already, this requires you to cover all the program paths during the JVM execution which could possibly result in a reflective call. With Minecraft, this is nearly impossible. The application is closed-source so we can’t just look at the source code to determine all reflective accesses. While decompilation of compiled code is possible, the code has been obfuscated by the development team beforehand, randomizing as much information as possible. Therefore, without having in-depth access to the source code we are not able to determine if every class, method, or field that could be accessed through reflections has actually been caught. This is why we opted for tracing a regular playthrough session of Minecraft to cover the basics at first. While the agent was helpful and already registered over 340 different reflective accesses for the reflection config just from that one session, we soon discovered a few bugs within Native Image while testing the build result. However, with the help of the GraalVM team, these were promptly fixed. In the manual testing process, we noticed server crashes due to non-available classes or methods that were apparently not required for the initial play session recorded with the tracing agent. We then included these required accesses manually in the configuration file for future build attempts. This was repeated until we were able to complete the game by beating the Ender Dragon as the official final boss of Minecraft. Using the configuration files created by this procedure everyone can create working servers at home.

How to Build a Native Minecraft Server

Native Minecraft Server running on a free ARM Ampere A1 Compute instance on Oracle Cloud.

We have set up this repository on GitHub in case you want to try this out yourself.

The repository comes with a short README.md file that explains everything you need to do in order to get started. After cloning, you can run a simple build script in the root directory, given that you have GraalVM 22.2 or later installed on your system. The build script will automatically download Minecraft’s server.jar, use the provided GraalVM Native Image configuration files, and build a native executable just for you. We have even included a compression step to further reduce deployment size if you want the smallest possible executables. To start the native Minecraft server, simply run the ./native-minecraft-server file.

Pro tip: The Oracle Cloud Free Tier not only provides four Ampere A1 cores and 24 GB of RAM always free, which is plenty for hosting a Minecraft server, but also free access to GraalVM Enterprise, which provides better performance and lower memory usage than the Community Edition.

Happy Minecrafting!

Preliminary Results

AOT-compilation as provided by GraalVM Native Image promises smaller artifacts as well as improved startup times and memory footprint, without compromising run-time performance.

A native Minecraft server is less than 120MB in size, and with that significantly smaller than Minecraft’s server.jar plus a JDK required to run it. With upx compression, the size of the native executable can be reduced even further to less than 40MB, which is smaller than just the server.jar.

Initial experiments also suggest competitive run-time performance with a reduced memory footprint. We initially used Native Image’s quick build mode to speed up development cycles and even though this mode is not intended to be used in production as it disables important performance optimizations, we were able to play the game with a single user just fine.

Furthermore, we found that startup times are rather difficult to measure for the Minecraft server. The reason for this is that the “Preparing spawn area” phase as performed by the server on startup appears to be significantly more computationally expensive than the time it takes to start a JVM. While Native Image can reduce JVM startup, we found that using the Community Edition of Native Image can result in overall slower Minecraft server startup times compared with it running on the JVM. With GraalVM Enterprise Native Image, however, we were able to build native Minecraft servers that can start up faster compared with running on the JVM.

The Enterprise Edition of Native Image also provides additional AOT compilation optimizations and features including the multi-threaded G1 garbage collector and Profile-Guided Optimizations (PGO) that can significantly boost the performance of native Minecraft servers. Even with the Serial GC that GraalVM Native Image uses by default, we also measured noticeable reductions in terms of memory footprint of up to 43%. Considering that these are preliminary results, we are optimistic that Minecraft server deployments can benefit from GraalVM Native Image.

Since the Minecraft server is a complex, real-world application, the GraalVM team has shown interest in using it as a benchmark target for GraalVM Native Image. We also hope that the Minecraft community builds on our work and helps benchmark different configurations for native Minecraft servers in more detail and in larger settings. Please feel free to open an issue with any benchmark results you have or in case you have any questions.

Outlook

In addition to more testing and benchmarking, the native Minecraft server could be further improved in different ways. The build script and configuration could be updated with support for the most recent version of Minecraft (1.19 at the time of writing). Since modding is a big part of the Minecraft community, it would also be interesting to investigate which server-side mods can be supported. The build script currently also only works for Linux and macOS. Building a native Minecraft server on Windows should be possible but would require some scripting work. Additional work is also required to add support for the server’s GUI. At the moment, it can only run in the nogui mode. Pull requests are welcome if you’d like to contribute any of this.

As part of this project, we set out to enable AOT-compilation of the Minecraft server using GraalVM Native Image. A build script and appropriate configuration for Native Image to do this are available on GitHub.

Please feel free to give it a try and share your experience with us, the GraalVM community, and the Minecraft community!

--

--

Jonathan Grenda
graalvm
Writer for

HPI IT-Systems-Engineering Master’s Student and passionate Software Developer.