A New GraalVM Release and New Free License!

Alina Yurenko
graalvm
Published in
11 min readJun 13, 2023

Oracle GraalVM

We are introducing a new distribution — Oracle GraalVM. It is available for JDK 17 and JDK 20 and released under the GraalVM Free Terms and Conditions (GFTC) license (also see the FAQ). This means that you can use all the greatest GraalVM features, both for development and in production, for free!

For Native Image, in this release we are introducing:

  • Profile-guided optimizations and more compiler optimizations for best peak performance
  • G1 GC for running applications with large heaps and minimal pause times
  • Compressed object headers and pointers for an even lower memory footprint
  • Machine learning to infer profiling information automatically
  • Additional security features with SBOM support

GraalVM Native Image is already known for its instant startup, but with this release we can demonstrate that it is also an incredibly efficient way to run long-running Java applications with high performance. We have tested our release on a Java 20 Spring PetClinic example application compiled with GraalVM Native Image, and compared it to JIT compilation, with the following performance benefits:

Performance of Spring Petclinic with Oracle GraalVM Native Image, GraalVM CE 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. Maximum throughput is measured using wrk, hitting five different endpoints (creations of pets, owners and clinic visits and reading owners and pet ids) in a round-robin way at 600 reqs/sec during 100 seconds. Peak throughput is the average performance for the following 180 seconds. Latency numbers are captured after the throughput experiment completes, applying a constant load of 5000 requests per second using wrk2.

Now let’s look closer at each of those performance metrics.

Native Image is known for the fastest application startup times. Thanks to additional compiler optimizations with Oracle GraalVM, startup goes to the next level:

  • 46.42% faster startup on Oracle GraalVM Native Image compared to GraalVM CE Native Image
  • 32 times faster startup than running on JIT (GraalVM CE with C2 JIT).

Thanks to additional memory optimizations, our benchmark application built with Oracle GraalVM Native Image uses significantly less memory:

  • 25% less memory than with GraalVM CE Native Image
  • 2.52 times less memory than with GraalVM CE with C2 JIT!

The peak throughput of Oracle GraalVM Native Image here is 1.6x higher than GraalVM CE. For this specific application, the throughput is still slightly below JIT. When running other benchmarks with Spring, as well as Quarkus and Micronaut, in many cases we see AOT even ahead of JIT for peak throughput.

Another metric important to evaluate for your application is efficiency: how much performance you can get for a given amount of time and resources. This is especially relevant for the cloud, where you pay for resources and how long you are using them, but even on-prem you want to use your infrastructure efficiently. To evaluate this, let’s look at how many requests our application can process per GB of memory per second (see the chart above):

  • Oracle GraalVM Native Image can process 14780 requests/GB/s, twice as much as both CE Native Image and C2 JIT.

This means that with Oracle GraalVM Native Image you can get the best peak throughput for the infrastructure you have, or run with good performance even in resource-constrained environments.

Now let’s look into latency distributions. Latency is a critical metric that reflects the responsiveness of all requests of your application. The slowest requests are visible in the tail latency distribution and impact greatly the quality of service. Usually, business requirements mandate a specific latency limit for most of the requests (typically 99%, i.e. the P99 latency).

Here we see that up until the 95th percentile Oracle GraalVM Native Image and CE JIT with C2 have very similar response times under 3 ms. This means that you can expect the same responsiveness of your application with Native Image as with JIT. However, the highest percentiles degrade significantly on the JIT while they stay low with Native Image. So for this application Native Image provides a significantly better P99 latency, and therefore improved responsiveness of the service.

In summary, Oracle GraalVM Native Image provides excellent performance characteristics for long-running applications in addition to fast startup and low resources usage.

Note that for GraalVM Community nothing changes — it is released under the same open-source license as before, and you can get the latest release from GitHub.

We’ve also been working on many new features in this release. Since we have so many updates, we have two blog posts: this one focuses on GraalVM JDK and Native Image, and we have another for GraalVM languages and Truffle updates.

Now let’s see what else is new in this release!

GraalVM for JDK 17 and JDK 20

GraalVM now has a new naming scheme that is aligned with JDK versioning — for example, this release includes two GraalVM versions: GraalVM for JDK 17 and GraalVM for JDK 20. Both versions are based on the main branch and contain new features, so you can choose the JDK version that works best for you. However, we encourage you to move to JDK 20 — as we announced some time ago, we are transitioning to a new release schedule, in which we’ll release GraalVM only for the latest Java version (just like Oracle JDK does). This way you can also use all the latest Java and GraalVM features together.

Another great update in this release is that the GraalVM JDK download now includes Native Image! No need to download Native Image as a separate component or run gu install native-image — it’s ready from the get-go.

And downloading Oracle GraalVM got easier as well — we now have stable scriptable download URLs, so you can use them to download directly, or in your scripts and Docker files. For example, to get Oracle GraalVM for JDK 20 on Linux x86–64, run the following command:

wget https://download.oracle.com/graalvm/20/latest/graalvm-jdk-20_linux-x64_bin.tar.gz

Machine Learning for Compiler Optimizations

One of the very helpful features available in Oracle GraalVM is profile-guided optimization (PGO). PGO enables you to collect an application’s profiling information at runtime and then use it with Native Image to optimize the performance of the resulting native executable. It enables you to combine the best of both worlds: the power of AOT optimizations together with runtime profiling.

One of the exciting new features in this release is machine-learning-based profile inference. Native Image now uses a pre-trained ML model to predict the probabilities of the control flow graph branches. Then, we use the predicted probabilities to perform PGO. In our measurements on a comprehensive set of benchmarks such as Renaissance, Da Capo, and Da Capo con Scala, PGO powered by the predicted profiles provides ~6% runtime speedup compared to the default Native Image configuration. ML-based profile inference is available in Oracle GraalVM, enabled by default, and we are looking forward to your feedback and performance reports.

However, remember that ML profile inference might slightly increase the size of the native executable file by 1–2%.

Even More Performance in Native Image

One of the ways a compiler can provide better performance is by leveraging the architecture-specific features of your deployment platform. Using those specific features is great for performance, but may pose compatibility challenges when deploying on machines without those specific instructions. We introduced a new -march option, similar to gcc, that gives you the flexibility to choose between platform-specific performance and compatibility: -march=compatibility for best compatibility, or -march=native for best performance if the native executable is deployed on the same machine architecture or on a machine with the same CPU features. To list all available machine architectures, use -march=list.

We’ve also made several changes to profile-guided optimizations:

  • To provide the best peak performance for your application, it’s essential for PGO to have its complete profiling information. This means that you may need to run your application several times with differing workloads to ensure that all its use cases are profiled. Now profiles collected during multiple PGO runs can be merged with native-image-configure.
  • Profile-guided optimizations now have a new sampling profiler that collects call stacks. This data is then included in an .iprof file. When the PGO-instrumented executable is built, the sampling profiler is turned on by default (but can be disabled if necessary). Keep in mind that to get good profiles and therefore good peak performance, you need to run relevant workloads and properly warm up your application.
  • We also implemented a context-aware inliner optimization, which uses the new sampling profiler to better invest optimization effort into hot code. The results are 2–7% smaller executable size and increased peak performance.
  • We improved loop vectorization support for Native Image, allowing the compiler to vectorize more loops for faster execution and bringing the performance of certain code shapes in AOT on par with JIT.

Native Image Bundles

The Native Image build command (native-image -jar MyApp.jar) may look simple on the surface, but it initiates a complex build process with many variables and properties that depend on the environment. It would be very helpful to be able to replicate a build — for example, for updating and patching your applications, or reproducing issues. For that purpose, this release introduced Native Image Bundles. Now you can produce a build bundle that contains a JAR file of your application along with information about arguments, environment variables, system properties settings, classpath and module-path options. To produce a bundle, use the — bundle-create=<imagename>.nib flag when building a native executable. This will produce the actual executable just like before, along with a <imagename>.nib file and the <imagename>.output directory. Having those files available, you can then reproduce the build by running the following:

native-image -bundle-apply=…/path/to/<imagename>.nib

Bundles should simplify updating Native Image deployments and help with debugging and reproducing issues. Give this feature a try and let us know what you think!

Build reports

Native Image now can generate build reports that help you better understand the contents of your executables. The build reports come in HTML format and provide insights into the following:

  • Information on the build environment and resources used
  • Analysis results including reachable types, fields, and methods
  • Full breakdown of the code and heap areas
  • Software Bill of Materials (SBOM)
Build report summary and code area breakdown

Use -H:+BuildReport to try this new experimental feature.

AWT on Linux

We’ve improved AWT support in Native Image: in addition to Windows, it now works on Linux! This means that more GUI Java applications will now work on Native Image. We are looking forward to seeing your applications running with Native Image and your feedback. Improved AWT support for macOS is also on the way.

An AWT app built as a native executable for Linux with GraalVM

Native Image Developer Experience

With every release we are introducing updates to make the developer experience with GraalVM, and in particular Native Image, more smooth and streamlined. There are several changes in this release:

  • Improved memory footprint of the Native Image build process. The builder now takes available memory into account to reduce memory pressure when many other processes are running on the same machine. It also consumes less memory in many cases and is therefore also less likely to fail due to out-of-memory errors. At the same time, we increased the maximum amount of memory Native Image can use, which will help when building large applications.
  • Great news for our Windows users! Native Image now sets up build environments for Windows automatically — you no longer need to use an x64 Native Tools Command Prompt. We also improved debugging on Windows: debug information now includes information about Java types.
  • We continue to build out our JFR support. Now the following events are also supported: ExecutionSample, ObjectAllocationInNewTLAB and JavaMonitorInflate.
  • We worked on making metadata composition safe, so that adding new metadata will never break a build.
  • Internal errors got more user-friendly. In case of failure, the Native Image build will no longer just show a stack trace, but will provide an error report and instructions on how to report the issue.
  • It is now possible to use GraalVM Native Image on RISC-V via the LLVM backend — read more about it in the related blog post.

New Monitoring Features in Native Image 📈

By a popular demand, together with the community we implemented remote management over JMX. It can be enabled with the --enable-monitoringoption, for example: --enable-monitoring=jmxclient,jmxserver. Note that this feature is experimental.

Ready for Native Image ✅

Sometimes we get a question “how do I know whether I can use a library X with Native Image”. Now at graalvm.org you can find a list of libraries and frameworks that are tested to work with Native Image out of the box. At the moment, the list contains more than 150 such libraries and frameworks that are verified to work with Native image, including Micronaut, Spring, Quarkus, Helidon, H2, GraphQL, MariaDB, Netty, MySQL, Neo4j, PostgreSQL, Testcontainers, Thymeleaf, and many others. We are glad to see our ecosystem actively adopting Native Image and providing support for it, in particular via the GraalVM-reachability-metadata repository. This repository is also integrated with Native Build Tools, so it’s now possible to automatically discover and pull Native Image configuration files for your dependencies.

GraalVM JDK and Compiler Updates

  • GraalVM JIT now supports ZGC! ZGC is a low latency garbage collector that targets applications requiring low latency or large heaps. (At the moment, ZGC is not supported in Native Image.) To enable it, use the command line option -XX:+UseZGC. We expect this update to be beneficial for larger applications, or in cases where low latency is critical, and are looking forward to your feedback and performance reports.
  • We are also open-sourcing the Ideal Graph Visualizer (IGV)!🎉 IGV is a developer tool that enables you to analyze compilation graphs and investigate performance issues. We hope IGV will be particularly helpful for those of you who are implementing languages on top of GraalVM, or investigating advanced compilation issues.

Community Contributions

  • There were almost 200 community contributions to the graalvm-reachability-metadata repository, which makes Java libraries available in Native Image. We want to particularly thank the Spring and Micronaut teams, who contributed a lot.
  • Together with Red Hat we continue to build out JFR support, improved debugging on Windows, and added experimental JMX support.
  • All your feedback, performance benchmarks and issue reports help us further improve GraalVM with every release.

What’s New in the GraalVM Ecosystem

  • We published a big overview of the GraalVM ecosystem based on your responses to a community survey: the most used features and tools, libraries and frameworks adoption, programming languages, and more: https://medium.com/graalvm/graalvm-community-survey-2022-results-328d0404d36e.
  • Spring Boot released a major version with support for Native Image out of the box! 📦
  • Quarkus released Quarkus 3 with a lot of new cool features; my favorite might be the new improved and extensible Dev UI — give it a try!
  • Micronaut will soon release the 4.0 version with Java 17 baseline and faster builds — you can already try it with Milestone 2.
  • You can now monitor GraalVM and Spring Boot applications using Azure App Insights.
  • Testcontainers work with Native Image via the GraalVM Reachability repository, and also several frameworks offer smooth integration — as an example see how Micronaut is testing the RabbitMQ integration in both Native Image and JVM modes.
  • Ionut Balosin and Florin Blanaru have published an independent (and very thorough) performance study of GraalVM vs OpenJDK C2.
  • We also saw interesting experiments with Apache Kafka, Quarkus, and GraalVM: a single broker node is up and running in ~130ms and with 60MB RSS.

Conclusion

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 navigate to the related blog post.

Now go ahead and try the new GraalVM!🚀

— the GraalVM team

--

--

Alina Yurenko
graalvm

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