Welcome, GraalVM for JDK 22!🚀

Alina Yurenko
graalvm
Published in
8 min readMar 19, 2024

Today we are releasing GraalVM for JDK 22!

As always, we release GraalVM on the same day that Oracle JDK and OpenJDK are released, so you immediately have access to the latest of both projects in one place. In terms of support for Java 22 features, most of them are available for Graal JIT as well as Native Image, and we are actively working on the remainder for upcoming releases— stay tuned!

Availability of Java 22 features on GraalVM

You can already download GraalVM and check the release notes for more details. Keep reading this blog post to see what’s new in this release!

Alternatively, you can watch our live stream recording:

Parsing 1 Billion Rows in Java in 0.3 Seconds 🔥

If you are from the Java world, chances are you have heard about 1BRC — a challenge by Gunnar Morling to see how modern Java can be used to process a file containing one billion rows as fast as possible. It seems like for two months the entire Java performance community had their eyes on this challenge (and so did we!). There’s much to learn from this challenge, which we will get to in a second, but first, let’s look at the results. The baseline solution proposed by Gunnar took 04:49 minutes to process the file; then contestants, applying various performance optimizations, reduced the time to as low as 01.53s on 8 cores, and absolutely impressive 0.3s on 32 cores!

The 1BRC challenge results (top-5)

You can see how the vast majority of the fastest solutions use GraalVM, and in particular Native Image. To quote Marko Topolnik from QuestDB: “Before we touch the code, there’s a low-effort way to speed up your program: use a modernized JVM. … During the 1BRC challenge, we found that GraalVM is one damn fast JVM.” Here’s some more feedback from the community:

twitter.com/jerrinot/status/1745553984113287538
twitter.com/fbrasisil/status/1744390068276748755
twitter.com/ElliotBarlas/status/1755284079048823144

What’s interesting is that Native Image not only gets the job done fast, but also uses fewer CPU resources to do it. We measured Thomas Wuerthinger’s solution running on the JVM and Native Image, and while the JVM took around 125 CPU cycles to process a row, Native Image took only 78 CPU cycles (even without PGO). Native Image makes your Java applications both faster and greener! 🌿

To learn more about the specific optimization techniques used by the participants, such as parallelization, SIMD, and branchless code, read the InfoQ article, or watch Nicolai Parlog’s live stream recording.

In other related updates, the equals and hashCode methods for Java records now use method handles that are intrinsified. This significantly improves the performance of records in Native Image.

New Class Initialization Strategy

The new class initialization approach that was introduced with --strict-image-heapin GraalVM for JDK 21 has now become the default. All classes are now allowed to be used and initialized at build time, regardless of the class initialization configuration. For classes that are configured as --initialize-at-run-time but that also used at build time, this means that they will appear as uninitialized again at run time. Major frameworks have already picked up this new strategy, so we expect the transition to be smooth for most of the users.

To learn more about it, navigate to the GitHub PR.

Better Native Image Profiling with Flame Graphs 🔥

Native Image Build Reports can display various data about the generated binary file and the build process itself. Among other things, Build Reports can now visualize profiling information as a flamegraph. This is useful for exploring how different methods contribute to overall execution time, based on the samples collected during the instrumentation run.

To generate a build report with the visualization, pass the -H:+BuildReport and -H:+BuildReportSamplerFlamegraph options at the step when you build a PGO-optimized native executable (so make sure you also include --pgo and the name of the profiles file):

Profiling flamegraph

Read more about this feature in the documentation.

New PGO features

Profile-guided optimizations are essential for great Native Image performance, but collecting good ones is not always easy. To simplify this experience, we have introduced a new experimental build option that will help you evaluate the quality of generated profiles. By passing -H:+PGOPrintProfileQuality to the Native Image build, you will see a new line in step 5 — Inlining methods — of the build output:

GraalVM Native Image: Generating 'demo-app' (executable)...
...
[5/8] Inlining methods... [***] (0.4s @ 0.28GB)
Info: PGO: Profile applicability is 21.67%. Profile relevance is 72.66%. (6.8s @ 0.29GB)
...

Profile applicability is a metric that tells you how often the profile contains data about locations in the source code for which that data is requested. Profile relevance shows to what extent the profile matches the methods that we see in the application (as the profile could have been taken before introducing code changes). Their utility comes from observing the metrics change when re-using profile across builds or providing a different profile for the same build of the application.

Another important quality of a good profile is completeness, which is achieved by running your instrumented app with a relevant workload for a sufficient time and exercising as many relevant code paths as possible. Sometimes to achieve this you need several instrumented runs — in that case, you can merge several profiles. We have also extended the PGO documentation — check out the reference manual for a deep dive, or our recent blog post for an intro and a guide you can follow.

Native Image meets Project Panama 🚀

We have added experimental support for the Foreign Function & Memory API (part of “Project Panama”, JEP 454). The API must be explicitly enabled with -H:+ForeignAPISupport (also requires -H:+UnlockExperimentalVMOptions). Currently, it includes support for foreign memory functionality and foreign functions downcalls on the x64 architecture but we are actively working on full FFM API support. Modules that perform "restricted" native operations must be specified using the --enable-native-access option. Take a look at Foreign Function and Memory API in Native Image for a more detailed overview of the FFM API support.

Oracle GraalVM Buildpack 📦

We have added another integration for Oracle GraalVM, so now it’s easier to integrate it into your projects, in particular Spring Boot ones. As an example, add the following to the configuration section of your spring-boot-maven-plugin:

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<!-- ... -->
<buildpacks>
<buildpack>docker.io/paketobuildpacks/oracle</buildpack>
<buildpack>urn:cnb:builder:paketo-buildpacks/java-native-image</buildpack>
</buildpacks>
</image>
</configuration>
</plugin>

For Gradle instructions and more details, see the related blog post.

Native Image Developer Experience

  • Working with reflection in Native Image got easier: methods, fields, and constructors of the Java Object class, primitive classes, and array classes are now registered for reflection by default.
  • We have added support for the NATIVE_IMAGE_OPTIONS environment variable, which allows users and tools to pass additional arguments via the environment. Similar to JAVA_TOOL_OPTIONS, the value of the environment variable is prefixed to the options supplied to native-image. For example, you can use it to turn on quick build mode for all local GraalVM Native Image builds:
# for Bash
echo 'export NATIVE_IMAGE_OPTIONS="${NATIVE_IMAGE_OPTIONS} -Ob"' >> "${HOME}/.bashrc"

# for Zsh
echo 'export NATIVE_IMAGE_OPTIONS="${NATIVE_IMAGE_OPTIONS} -Ob"' >> "${HOME}/.zshrc"
  • We have improved how missing metadata is reported in Native Image: those cases now throw special exceptions (experimental). You can catch metadata exceptions early and debug related issues using the -H:ThrowMissingRegistrationErrors=<package-prefix> option (or -H:ThrowMissingRegistrationErrors for all packages).
  • We have added support for the following JFR events: AllocationRequiringGC, SystemGC, and ThreadAllocationStatistics. (Developed in collaboration with Red Hat)
  • Native Image now provides an API option for creating thread dumps --enable-monitoring=threaddump. The option -H:±DumpThreadStacksOnSignal is now deprecated and marked for removal.
  • We have improved the mechanism for tracking Native Image agent calls: the agent now tracks calls to ClassLoader.findSystemClass, ObjectInputStream.resolveClass, and Bundles.of, and registers resource bundles as bundle name-locale pairs.
  • We also published a step-by-step demo to help you build small container images with GraalVM: github.com/graalvm/graalvm-demos/tree/master/tiny-java-containers

Compiler Updates 🚀

As we work towards Project Galahad, the Graal JIT compiler options now use the jdk.graal. prefix (for example, -Djdk.graal.PrintCompilation=true). The legacy graal. prefix is deprecated but still can be used (for example, -Dgraal.PrintCompilation=true).

As with every release, we have worked on performance improvements, and it’s great to receive community feedback and performance reports. This time we saw a great performance study by Ionut Balosin and Florin Blanaru, where they compared the performance of various JIT compilers available in JDK 21 on a wide set of benchmarks, and their measurement show that, with Graal JIT, applications run 17% faster on arm64 and 23% faster on x64:

ionutbalosin.com/2024/02/jvm-performance-comparison-for-jdk-21/

Native Build Tools 🛠️

Native Build Tools (NBT) now enable access to the GraalVM Reachability Metadata repository by default. This means that for projects that use NBT (and frameworks that rely on NBT), configuration for known libraries will be resolved automatically. Using libraries just got easier! 🚀

We’ve also made several changes and improvements in the recent NBT releases — make sure you upgrade to 0.10.1, if you haven’t already.

Community and Ecosystem

  • Oracle NetSuite have completely moved to GraalVM JDK to improve performance and reduce resource consumption.
  • More than 2000 repositories on GitHub are now using the GraalVM GitHub action!🚀
  • The JHipster Lite project generator running on the latest GraalVM build starts in one second and uses only 140 MB of RAM, so it can run in production on a 0.25 vCPU.5 GB of RAM cloud instance!
  • Marcus Hellberg shared a quick and easy way to build an AI chatbot with Spring AI & Vaadin, that also starts in 0.1 seconds with GraalVM.
  • Langchain4j, a library that brings LLM capabilities to Java applications, works with GraalVM native applications! See the Quarkus and Spring Boot guides to get started.
  • You can make polyglot GraalVM applications even faster by leveraging GPUs via TornadoVM — see how.
  • You might know about MLE, the GraalVM embedding in Oracle Database that enables executing GraalVM languages, such as JavaScript, right in the database. A similar module for JavaScript execution is now also available in MySQL.
  • See how you can make your Java applications running on Cloud Run 10x faster and 3x smaller with GraalVM.
  • We see more and more libraries being developed Native-Image friendly from the start — check out native TypeMap as an example.

Conclusion

We’d like to take this opportunity to thank our amazing contributors and community for all the feedback, suggestions, and contributions that went into this release.

If you have feedback for this release or suggestions for features that you would like to see in future releases, please share them with us on Slack, GitHub, or Twitter.

Now go ahead and try the new GraalVM! 🚀

— the GraalVM team

--

--

Alina Yurenko
graalvm
Editor for

I love all things tech & community. Developer Advocate for @graalvm, blog posts about programming, open source, and devrel.