Announcing GraalVM 20.2.0

Oleg Šelajev
graalvm
Published in
7 min readAug 18, 2020

The most advanced and fastest polyglot runtime that shows absolutely superb results on Java and other JVM languages — and can run programs written in popular languages like JavaScript, Ruby, R, and Python — just got even better.

Today marks the release of GraalVM 20.2.0. This is a feature release and it packs a lot of improvements to performance, native image technology, and compatibility with the supported languages.

As always, we’re truly thankful to the amazing GraalVM community for collaboration, for providing feedback and raising issues, and for sending pull requests and talking about how you use GraalVM in your projects.

In this post we want to highlight some of the most notable changes in GraalVM 20.2.0. For a more detailed list please consider the release notes on the website.

Platform updates

In every release there are a number of fixes and improvements to every GraalVM component. The compiler is no exception. Let’s take a look at two new features you can try out in GraalVM 20.2.0:

In GraalVM Enterprise, the memory used by libgraal will now be released to the OS when an application enters a stable phase and compilation is idle. This is achieved by attaching each compiler thread to a libgraal isolate. The number of compiler threads that can be attached to an isolate is controlled by the jvmci.ThreadsPerNativeLibraryRuntime system property. The default is all threads attached to a single isolate. Isolates are initialized on demand to handle extra compiler threads. When a compiler thread becomes idle, which is configurable by the jvmci.CompilerIdleDelay system property, it detaches itself from its isolate. As the last thread detaches from an isolate, the isolate is shut down and its memory (i.e., the libgraal heap) is released to the OS. This feature has a number of advantages:

  • It can reduce the RSS memory footprint of a GraalVM process. If the compiler is not being used, it uses no resources.
  • If multiple isolates are used, it mitigates interference between complier threads. For example, there is less contention on shared objects, and a garbage collection in one isolate will not pause threads in other isolates.

Another notable addition to GraalVM Enterprise edition is a new, experimental, off-by-default partial loop unrolling optimization. Loop unrolling is a traditional compiler optimization that can improve performance for computational heavy, loop focused workloads.

Partial loop unrolling is an extended form of this optimization that works for counted loops with an unknown upper bound of loop iterations i. The transformation creates two versions of the loop: a main version of the loop that can be unrolled n times leading to an optimized loop body doing floor(i/n) iterations, and a fix-up loop doing the remaining number of i % n iterations.

Consider the following Java example:

for(int i = 0; i < upp; i++){
// body
}

Partial unrolling by a factor of 2 creates the two versions of the loop:

int i1=0;
int unrollFactor=2;
for(;i1<upp-unrollFactor;i1=i1+unrollFactor){
// body i1
// body i1 +1
}
int i2=i1;\
for(;i2<upp;i2++){
// body
}

The major benefit of partial unrolling is the reduced number of jumps and updates to the loop control values, i.e., fewer counter increments and loop header jumps.

You can experiment with the new optimization by enabling it with -Dgraal.EnterprisePartialUnroll=true. However, note that it is experimental and thus may lead to unknown errors. We appreciate all feedback, so if you discover any issues during your experiments, please don’t hesitate to report them to the GraalVM team.

Native image

GraalVM Native Image continues to be a very exciting feature, and in 20.2.0 it got several important updates. Two of the features we’d like to highlight here are deployment improvements:

It was already possible to generate native executables where system libraries like libc are statically linked. This feature allows you to create fully standalone executables that can be used, for example, in the minimal FROM SCRATCH Docker containers that don’t have anything else present.

In 20.2.0, static compilation with Muslc got reworked and now you don’t have to rely on third party Muslc bundles. But it does require having a working musl-gcc in the system where you generate the native executable.

If you don’t have it installed and ready, there’s an instruction on how to get it.

You can verify that it is installed correctly by running:

musl-gcc -v

Then, generating a native executable statically linked against muslc is a matter of providing the following arguments to the Native Image utility:

 — static — libc=musl

Then the Docker image for the application deployment could look something like this, assuming `my-native-app` is the generated executable:

FROM scratch
COPY my-native-app /my-native-app
ENTRYPOINT [“/my-native-app”]

It’ll create an empty Docker image and only place your application into it.

Another interesting way of deploying native executables into containers is to use small Docker images with a minimum of dependencies, preferably with no operating system. For example, distroless images come to mind.

A distroless/base image contains only the following:

  • ca-certificates
  • A /etc/passwd entry for a root user
  • A /tmp directory
  • tzdata
  • glibc
  • libssl
  • openssl

To build your native executable and statically link everything, except libc which is provided by the container, use the Native Image -H:+StaticExecutableWithDynamicLibC flag.

We also improved Native Image support for the G1-based garbage collector which is available in GraalVM Enterprise. If you would like to try a garbage collection implementation that lowers the GC pause times, specify your GC preference with the following option: -H:+UseLowLatencyGC at build time. The native executable will build normally, and at runtime you can configure its memory usage with the usual flags -Xmx and monitor the GC activity with the -XX:+PrintGC or -XX:+VerboseGC.

Last, but not least, there’s a potentially breaking change in 20.2.0: the class initialization strategy has been changed to include fewer classes from the JDK library to be initialized at build time.

If you are initializing your application classes at build time, they might trigger initialization of the JDK library classes at build time, which will conflict with the new strategy, and the build will not succeed. You can solve this problem by introducing the build time class initialization for necessary classes, yourself.

For example, for a sample Micronaut application, I needed to add — initialize-at-build-time=com.sun.org.apache.xerces,com.sun.xml.internal,jdk.xml.internal to the command line to initialize those classes at build time, like prior to the 20.2.0 release.

If you’re relying on a framework or library to configure your native image generation, you might need to update it to a more recent version that works with the 20.2.0.

Truffle

One important change to the Truffle language implementation framework was changing the default inlining heuristic for the language interpreters in which inlining budgets are based on Graal IR node counts and not Truffle Node counts.
This is a very interesting change that makes inlining, which is a very important optimization, work better at estimating the cost of the optimization. A Truffle node can be of arbitrary complexity and overspend the inlining budget by “tricking” the inlining heuristic into counting it as just one node. So estimating code size for inlining based on the Truffle node count could lead to inefficient results.

A very welcome addition to the language implementation API available in 20.2.0 is the DynamicObject model, and library to make use of it. You can find more details in the docs, but in a nutshell — they allow language implementations better control over the layout of objects in dynamic languages, and accommodate dynamically adding members or changing types. This allows better handling of objects by shapes and makes accessing object properties more efficient.

JavaScript

In GraalVM 20.2.0 we updated Node.js to version 12.18.0. We also implemented a number of proposals: Intl.NumberFormat Unified API, the Logical Assignment Operators, the Top-level Await proposal, even the Promise.any proposal (available with the ECMAScript 2021 mode).

There’s also a freshly implemented support for async stack traces that you can try.

Ruby

We’d like to highlight the compatibility improvements in TruffleRuby in GraalVM 20.2.0. Besides a ton of other changes you can see in the release notes, now the Ripper stdlib has been implemented using the C extension.

This release has also improved compatibility (notably refinements) and updated to Ruby 2.6.6, added better interoperability support with other languages, and includes various performance improvements.

Python

Among the most notable changes in GraalPython, we implemented better reference counting for native extensions to fix memory leaks.

We also introduced code serialization to .pyc files (.pyc files persistently store the Python bytecode compiled from the source .py files), which should improve startup of Python programs.

GraalVM Enterprise Python implementation now also includes a faster implementation of _struct.

Tools

Tools are a very important part of the GraalVM project. In 20.2.0 there was a number of improvements to GraalVM support in VSCode.

The Language Server Protocol implementation now supports latest protocol version 3.15. GraalVM LSP is a very interesting addition to the usual LSP implementation in VS Code — it can notice dynamic information only available at the runtime of your program and use that to add suggestions to the content assist in the IDE for you.

All GraalVM VSCode extensions were merged into one, which you can obtain from the marketplace.

VSCode can also now show code coverage from GraalVM LSP.

VisualVM now includes a GoToSource action, so you can jump from the profiler into your preferred IDE. This is a welcome addition to quickly navigate to the place in your code that requires the most attention.

If you configure it with the source roots location:

And right click on the class or method name in one of the views, for example in the CPU profiler:

It opens that file in your favourite IDE:

These are only some of the improvements we are so proud of in the GraalVM 20.2.0 feature release! Please read a more detailed outline of the new and noteworthy features in the release notes and consider looking at the changelog for the project components on GitHub!

Download GraalVM 20.2 or GraalVM Enterprise 20.2, use it for running your applications, and build new, exciting projects. If you have any feedback please don’t hesitate to let us know on Twitter, Slack or GitHub!

— GraalVM Team

--

--