GraalVM for JDK 21 is here! 🚀

Alina Yurenko
Published in
7 min readSep 19


Today we are releasing GraalVM for JDK 21!

This release brings many new GraalVM features, as well as Java 21 features. For example, virtual threads from Project Loom are now fully supported on GraalVM, including Native Image — you no longer need to enable them as a preview feature.

As always, you can get the builds from

Now, let’s look at what’s in this release! Keep reading to learn what’s new in Native Image and GraalVM JDK, and go to the related blog post for updates to Truffle and GraalVM languages.

You can also watch what’s new in this release in our live stream recording:

AOT at the Speed of JIT

In our previous release blog post, we shared performance comparisons on the example of Spring Petclinic: we showed how Native Image is ahead of JIT in several performance metrics, such as startup, memory usage, requests/GB-s, and latency. With this release, for the same Petclinic app we see Native Image outperforming JIT.

The application running on Oracle GraalVM Native Image with profile-guided optimizations (PGO) and the G1 GC achieves a peak throughput of 13'075 requests per second, while the same application running on JIT with C2 JIT compiler and the same G1 GC achieves only 12'488 requests per second.

Performance of Spring Petclinic with Oracle GraalVM Native Image and GraalVM CE with C2 JIT. The benchmarking experiment ran the latest Spring Petclinic on Oracle X5–2 servers (Intel Xeon E5–2699 v3), restricting the workload to 16 CPUs and setting a maximum heap size of 1GB.

Some more performance observations from this benchmark ( running with the heap size of 512MB):

Performance of Spring Petclinic with Oracle GraalVM Native Image and GraalVM CE with C2 JIT. The benchmarking experiment ran the latest Spring Petclinic on Oracle X5–2 servers (Intel Xeon E5–2699 v3), restricting the workload to 16 CPUs and setting a maximum heap size of 512MB.

The chart below shows the correlation between ops/GB*s and specific maximum Java heap (via -Xmx). You can see how Oracle Native Image consistently provides more throughput per compute resource for all heap sizes. So applications of all sizes will see more throughput per memory with Oracle GraalVM Native Image, but specifically, the applications with small and medium heaps, up and around 1GB, can get almost twice as much throughput per compute resources as when running on JIT:

So now with Native Image you can maximize all performance aspects: peak throughput, latency, memory usage, startup, and packaging size. Give it a try yourself and feel free to share your benchmark results with us.

As always, there are more Native Image performance updates in this release.

G1 GC now works on Linux AArch64 (in addition to Linux x64). You can enable it by passing --gc=G1 to Native Image via the command line or in your build file. While there are many possible GC strategies, for those looking for the best peak performance we recommend evaluating the combination of G1 and PGO.

New optimization levels policy. There are several optimization levels in Native Image you can choose from. You might be familiar with -Ob, the quick build mode, which can be used in development for faster builds. When you build your application for production, you should compile with more optimizations for better performance— when no custom option is provided, Native Image defaults to -O2. This level of optimization provides high performance, 10–15% faster compile times, and has been tuned for common Java microservice workloads.

Now we are introducing a new optimization level in Oracle GraalVM, -O3, which applies maximum optimizations for the best peak performance. You can enable this optimization level yourself, and if you’re using PGO, it will be triggered automatically.

Developer Experience

  • You can now use the option --parallelism to control how many threads are used by the build process — for example, --parallelism=4.
  • There are several other updates to the build output — it now shows the memory and thread limits of the build process, and you can control the color of output with --color.
  • We’ve made changes to the memory limits of the Native Image build process. By default, the build process only uses free memory: this approach is intended to avoid slowing down your machine and other processes. If free memory is less than 8GB, the build process will use 85% of total memory. So if you observe your machine being slow during native builds, consider closing those processes you don’t need.
  • native-image-inspect in Oracle GraalVM can now dump the heap of executables and shared libraries: native-image-inspect --heap path/to/image.
  • Special exception handling for missing metadata. Previously, missing metadata could lead to program misbehavior that was hard to debug. Now in cases when the metadata entry is missing, the call that was requiring it will fail with the special exception — for example, MissingReflectionMetadataException. There are also similar exceptions for missing resources and serialization metadata.
  • New Class Initialization Strategy. In this release, we are introducing a new class initialization approach that will become the default in the future: 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 this means that they will appear as uninitialized again at run time. To enable the new approach, pass the --strict-image-heap flag to Native Image. In case you run into errors or issues with it, we will share more details in a blog post that will go out together with the October 24 patch release.
  • gu is no longer needed for Native Image and GraalVM languages — learn how to use additional components now.
  • Support for the JFR event ThreadCPULoad was contributed by BellSoft.

Experimental Build Options in Native Image

There are many build options in Native Image, most of which are stable, and some are experimental. To provide better awareness and control over options that are being used and avoid misuse, in this release we have added a new -H:±UnlockExperimentalVMOptions option to explicitly handle experimental options. Here’s how you can unlock and lock experimental options:

native-image -H:+UnlockExperimentalVMOptions -H:SomeExperimentalOption \
-H:-UnlockExperimentalVMOptions -cp ./ HelloWorld

Explicit enabling of experimental options will become required in the next release, but you can test this already in GraalVM for JDK 21 using the environment variable NATIVE_IMAGE_EXPERIMENTAL_OPTIONS_ARE_FATAL=true.

As we are stabilizing the public API, we encourage your feedback on which experimental features you are actively using in the corresponding roadmap ticket.

Control Flow Integrity in Native Image

Oracle GraalVM now provides a form of control flow integrity (CFI) using ARM Pointer Authentication Code (PAC) on supported hardware. PAC mitigates return-oriented programming (ROP) attacks by ensuring the integrity of the return address of a function call: the return address is signed on function entry and the signature is checked before function return. Any tampering is detected by the CPU. You can enable it with the -H:EnableCFI option.

JDK and Compiler updates

We implemented an initial optimization of Java Vector API (JEP 338) operations in Oracle GraalVM. Multiple operations on the JVM are now transformed to efficient machine instructions where possible. This optimization is enabled by default (you can disable it with -Dgraal.OptimizeVectorAPI=false) and we welcome your feedback.

We also implemented several intrinsics for JDK 21 for both x64 and AArch64 architectures.

Refactoring GraalVM SDK

The GraalVM SDK is split into four fine-grained modules:

  • org.graalvm.nativeimage: The public API of Native Image for advanced use cases.
  • org.graalvm.polyglot: A library that allows users to embed polyglot language implementations in Java.
  • org.graalvm.word: A low-level library for machine-word-sized values in Java.
  • org.graalvm.collections: A collections library for GraalVM components.

Note that all GraalVM APIs remain compatible in this release.

Ecosystem and Community


We want to take this opportunity to thank our community for all the feedback, suggestions, and contributions that went into this release. If you have additional feedback on 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.

And for the GraalVM languages and Truffle updates, please visit the related blog post.

Now go ahead and try the new GraalVM! 🚀

— the GraalVM team



Alina Yurenko

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