GraalVM is a high-performance, embeddable, polyglot Virtual Machine for running applications written in JavaScript, Python, Ruby, R, JVM-based languages like Java, Scala, Kotlin, and LLVM-based languages such as C and C++.
Additionally, GraalVM allows efficient interoperability between programming languages and compiling Java applications ahead-of-time into native executables for faster startup time and lower memory overhead.
GraalVM是一个高性能的、可嵌入的、多语言的虚拟机,用于运行用JavaScript、Python、Ruby、R、基于 JVM 的语言(如Java、Scala、Kotlin)和基于LLVM 的语言(如C和C++)编写的应用程序。
此外,GraalVM允许编程语言之间的高效互操作性,以及提前将Java应用程序编译为本机可执行程序,从而加快启动时间和降低内存开销。
GraalVM消除了编程语言之间的隔离,并支持共享运行时中的互操作性。它可以独立运行,也可以在OpenJDK、Node.js或Oracle数据库上下文中运行。
Polyglot
Zero overhead interoperability between programming languages allows you to write polyglot applications and select the best language for your task.
Native
Native images compiled with GraalVM ahead-of-time improve the startup time and reduce the memory footprint of JVM-based applications.
GraalVM can be embedded in both managed and native applications. There are existing integrations into OpenJDK, Node.js and Oracle Database.
https://github.com/oracle/graal/releases
This download includes:
JVM
JavaScript Engine & Node.js Runtime
LLVM Engine
Developer Tools, including technology preview of VSCode extensions.
The Native Image, Ruby, R, Python, and llvm-toolchain plugins are optionally available using the GraalVM gu utility.
The release notes can be found on the graalvm.org website.
Get started with GraalVM and configure it to run programs written in Java (or other JVM-based languages), JavaScript and Node.js, Ruby, R, or Python. This page guides you through downloading and installing GraalVM and adding support for additional languages, and shows you how to run simple programs on GraalVM.
GraalVM is a standalone Java Development Kit to execute Java or JVM-based languages (e.g. Scala, Kotlin), dynamic languages (e.g. JavaScript, R, Ruby, R, Python), LLVM-based languages (e.g. C and C++) in one shared runtime, and supports Linux and macOS platforms on x86 64-bit systems. It is available as Community Edition (CE) and Enterprise Edition (EE). GraalVM Community Edition is based on the OpenJDK 8. GraalVM Enterprise Edition is developed on top of the Java SE 1.8.0_221. Proceed to the Downloads page and make your choice depending on your operating system and use case.
In this getting started guide we will focus on working with GraalVM Enterprise Edition, obtained from the Oracle Technology Network. The base installation includes the JVM, the GraalVM compiler, the LLVM bitcode interpreter, and the JavaScript runtime with Node.js support – all in one package. GraalVM environment can be extended with:
Getting GraalVM installed and ready-to-go should only take a few minutes. Please note, unlike Java HotSpot VM or OpenJDK distributions for macOS that come as a .dmg file, GraalVM does not provide the installation wizard.
$ tar -xvf archive.tar.gz
bin
folder to the PATH
environment variable: $ export PATH=/Contents/Home/bin:$PATH
. Verify whether you are using GraalVM with the echo command: $ echo $PATH
.JAVA_HOME
environment variable to resolve to the GraalVM installation directory: $ export JAVA_HOME=/Contents/Home
.Optionally set the JAVA_HOME
environment variable to resolve to the GraalVM installation directory. You can also specify GraalVM as the JRE or JDK installation in your Java IDE.
Note on macOS java_home command
The information property file, Info.plist, is in the top level Contents folder. This means that GraalVM participates in the macOS specific /usr/libexec/java_home
mechanism. Depending on other JDK 8 installation(s) available, it is now possible that /usr/libexec/java_home -v1.8
returns /Library/Java/JavaVirtualMachines/graalvm-ee-19.2.0.1/Contents/Home
. You can run /usr/libexec/java_home -v1.8 -V
to see the complete list of 1.8 JVMs available to the java_home
command. This command appears to sort the JVMs in decreasing version order and chooses the top one as the default for the specified version. Within a specific version, the sort order appears to be stable but is unspecified.
Since the executables of all language runtimes in GraalVM emulate the behavior of the languages’ default runtimes, setting GraalVM on your PATH
should be enough to run an application with GraalVM.
GraalVM’s /bin
directory is similar to that of a standard JDK, but includes a set of additional launchers:
Notably, java runs the JVM with GraalVM’s default compiler. The Ruby, Python and R executables become available only if you install the corresponding language engines. For example, running the following command will install Ruby support:
gu install ruby
For more information on using GraalVM Updater please refer to its documentation.
Once the PATH
environment variable is set properly, it is easy to check language versions with GraalVM launchers:
$ java -version
$ java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit GraalVM EE 19.2.0 (build 25.221-b11-jvmci-19.2-b02, mixed mode)
$ node -v
v10.16.3
$ lli --version
LLVM (GraalVM EE Native 19.2.0.1)
If you have, e.g., the Ruby language component installed, you can check its version as well:
$ ruby -v
truffleruby 19.2.0.1, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin]
The official Docker images for GraalVM CE are available from the Docker Hub: https://hub.docker.com/r/oracle/graalvm-ce/.
If you want to use the Docker container with GraalVM CE, use the docker pull
command:
docker pull oracle/graalvm-ce:19.2.0.1
The image is based on Oracle Linux and has GraalVM CE downloaded, unzipped and made available. It means that Java, JavaScript, Node and the LLVM interpreter are available out of the box.
You can start a container and enter the bash
session with the following run command:
docker run -it oracle/graalvm-ce:19.2.0.1 bash
Check that java
, js
and other commands work as expected.
→ docker run -it oracle/graalvm-ce:19.2.0.1 bash
bash-4.2# java -version
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-20190711112007.graal.jdk8u-src-tar-gz-b08)
OpenJDK 64-Bit GraalVM CE 19.2.0.1 (build 25.222-b08-jvmci-19.2-b02, mixed mode)
bash-4.2# node
> 1 + 1
2
> process.exit()
bash-4.2# lli --version
LLVM (GraalVM CE Native 19.2.0.1)
bash-4.2#
Please note that the image contains only the components immediately available in the GraalVM CE distribution. However, the GraalVM Updater utility is on the PATH
. You can install the support for additional languages like Ruby, R, or Python at will. For example, the following command installs the Ruby support (the output below is truncated for brevity):
docker run -it oracle/graalvm-ce:19.2.0.1 bash
bash-4.2# gu install ruby
Downloading: Component catalog
Processing component archive: Component ruby
Downloading: Component ruby
[###### ]
...
If you want to mount a directory from the host system to have it locally available in the container, use Docker volumes.
Here is a sample command that maps the /absolute/path/to/dir/no/trailing/slash
directory from the host system to the /path/inside/container
inside the container.
docker run -it -v /absolute/path/to/dir/no/trailing/slash:/path/inside/container oracle/graalvm-ce:19.2.0.1 bash
If you want to create docker images that contain GraalVM Ruby, R, or Python implementation, you can use dockerfiles like the example below, which uses oracle/graalvm-ce:19.2.0.1
as the base image, installs Ruby support using the gu
utility, then creates and runs a sample Ruby program.
FROM oracle/graalvm-ce:19.2.0.1
RUN gu install ruby
WORKDIR /workdir
RUN echo 'puts "Hello from Truffleruby!\nVersion: #{RUBY_DESCRIPTION}"' > app.rb
CMD ruby app.rb
If you put the above snippet in the Dockerfile
in the current directory, you can build and run it with the following commands.
docker build -t truffleruby-demo .
...
$ docker run -it --rm truffleruby-demo
Hello from Truffleruby!
Version: truffleruby 19.2.0.1, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin]
Since the executables of all language runtimes in GraalVM emulate the behavior of the languages’ default runtimes, putting GraalVM on your PATH
should be enough to run your applications with GraalVM.
Take a look at this typical HelloWorld
class:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Run the following command to compile this class to bytecode and then run it on GraalVM:
$ javac HelloWorld.java
$ java HelloWorld
Hello World!
You can find a collection of larger examples of Java applications you can try running with GraalVM on the examples page.
GraalVM can execute plain JavaScript code, both in REPL mode and by executing script files directly.
$ js
> 1 + 2
3
GraalVM also supports running Node.js applications. More than 95,000 npm modules are tested and are fully compatible with GraalVM, including modules like express, react, async, request, browserify, grunt, mocha, and underscore. To install a Node.js module, use the npm
executable in the /bin
folder of the GraalVM package. The npm
command is equivalent to the default Node.js command and supports all Node.js APIs.
1. Install the colors
and ansispan
modules using npm install
:
npm install colors ansispan
After the modules are installed, you can use them from your application.
2. Add the following code snippet to a file named app.js
and save it in the same directory where you installed the Node.js modules:
const http = require("http");
const span = require("ansispan");
require("colors");
http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.end(span("Hello Graal.js!".green));
}).listen(8000, function() { console.log("Graal.js server running at http://127.0.0.1:8000/".red); });
3. Execute it on GraalVM using the node
command:
node app.js
More information on compatibility with the Node.js and configuring GraalVM read the reference manual on JavaScript in GraalVM.
The LLVM interpreter in GraalVM executes LLVM bitcode, and therefore any programming language that can be compiled to LLVM bitcode.
Put this C HelloWorld
into a file named hello.c
:
#include
int main() {
printf("Hello from GraalVM!\n");
return 0;
}
You can compile hello.c
to an LLVM bitcode file named hello.bc
using the following command:
$ clang -c -O1 -emit-llvm hello.c
You can then run hello.bc
on GraalVM like this:
$ lli hello.bc
Hello from GraalVM!
More examples and information on what languages and runtimes are supported can be found in the reference manual for LLVM.
The Ruby engine is not installed by default, but it can easily be added using GraalVM Updater:
gu install ruby
This makes Ruby commands like ruby
, gem
, irb
, rake
, rdoc
and ri
available:
$ ruby [options] program.rb
GraalVM Ruby implementation uses the same options as the standard implementation of Ruby, with some additions.
$ gem install chunky_png
$ ruby -r chunky_png -e "puts ChunkyPNG::Color.to_hex(ChunkyPNG::Color('mintcream @ 0.5'))"
#f5fffa80
Using Bundler
GraalVM implementation of Ruby ships with the Bundler environment. Therefore its installation, gem install bundler
, is not needed.
$ bundle exec ...
More examples and additional information on Ruby support in GraalVM can be found in the reference manual for Ruby.
The R engine is not installed by default, but it can easily be added using GraalVM Updater:
gu install R
When the R engine is installed, you can execute R scripts and use the R REPL with GraalVM:
$ R
R version 3.5.1 (FastR)
...
> 1 + 1
[1] 2
More examples and additional information on R support in GraalVM can be found in the reference manual for R.
GraalVM implementation of Python 3.7 has recently been started. The Python engine is not installed by default, but it can easily be added using GraalVM Updater:
gu install python
Once the Python engine is installed, GraalVM can execute Python programs:
$ graalpython
...
>>> 1 + 2
3
>>> exit()
More examples and additional information on Python support in GraalVM can be found in the reference manual for Python.
If enabled, using the --polyglot
flag, scripts executed on GraalVM can use interoperability features to call into other languages and exchange data with them.
For example, running js --jvm --polyglot example.js
executes example.js
in a polyglot context. If the program calls any code in other supported languages, GraalVM executes that code in the same runtime as the example.js
application. For more information on polyglot applications see the polyglot documentation.
GraalVM can compile Java bytecode into native images to achieve faster startup and smaller footprint for your applications. The Native Image functionality is not available by default, but it can easily be added using gu install native-image
command. Let’s use the HelloWorld
example from above to demonstrate how to compile Java bytecode into a native image:
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Run the following to compile the class to bytecode and then build a native image:
$ javac HelloWorld.java
$ native-image HelloWorld
This builds an executable file named helloworld
in the current working directory. Invoking it executes the natively compiled code of the HelloWorld
class as follows:
$ ./helloworld
Hello, World!
Polyglot Capabilities of Native Images
GraalVM Native Image Generator also makes it easy to use polyglot capabilities. Take this example of a JSON pretty-printer using the GraalVM implementation of JavaScript:
// PrettyPrintJSON.java
import java.io.*;
import java.util.stream.*;
import org.graalvm.polyglot.*;
public class PrettyPrintJSON {
public static void main(String[] args) throws java.io.IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String input = reader.lines().collect(Collectors.joining(System.lineSeparator()));
try (Context context = Context.create("js")) {
Value parse = context.eval("js", "JSON.parse");
Value stringify = context.eval("js", "JSON.stringify");
Value result = stringify.execute(parse.execute(input), null, 2);
System.out.println(result.asString());
}
}
}
The --language:js
argument ensures that the JavaScript engine is available in the generated image:
$ javac PrettyPrintJSON.java
$ native-image --language:js --initialize-at-build-time PrettyPrintJSON
The native image generation will take several minutes as it does not just build the PrettyPrintJSON
class, but includes building a JavaScript engine (along with the support for partial evaluation). Additionally, the image building requires large amounts of physical memory, especially if you build an image with Truffle Language Implementation Framework included, which is exactly the case here. Memory requirements and some other limitations of ahead-of-time compilation with GraalVM are listed here.
The resulting executable can now perform JSON pretty printing:
$ ./prettyprintjson <
Here is the JSON output from the native executable:
{
"GraalVM": {
"description": "Language Abstraction Platform",
"supports": [
"combining languages",
"embedding languages",
"creating native images"
],
"languages": [
"Java",
"JavaScript",
"Node.js",
"Python",
"Ruby",
"R",
"LLVM"
]
}
}
The native image is much faster than running the same code on the JVM directly:
$ time bin/java PrettyPrintJSON < test.json > /dev/null
real 0m1.101s
user 0m2.471s
sys 0m0.237s
$ time ./prettyprintjson < test.json > /dev/null
real 0m0.037s
user 0m0.015s
sys 0m0.016s
If you want to learn what GraalVM offers to different types of teams, read the Why GraalVM page. Some of the diverse features of GraalVM are disclosed and supported with examples in Top 10 Things To Do With GraalVM article. Or, you can examine different supported languages in action by looking at example applications. If you want to learn about the common tools GraalVM enables for the supported languages, proceed to the tools section of the reference manual. And if you are mostly interested in a specific language, more extensive documentation is available in the reference manual as well.
GraalVM gives you an enhanced performance for JVM-based applications written in languages such as Java, Scala, Groovy, Clojure or Kotlin. It uses a dynamic compiler that dramatically improves the efficiency and the speed of applications through unique approaches to code analysis and optimization.
In addition to running JVM-based languages, you can also call any other language implemented with GraalVM Language Implementation Framework directly from Java. See the Polyglot Reference and the Embedding documentation for more information about interoperability with other programming languages.
The GraalVM compiler achieves excellent performance for modern workloads such as Scala or usage of the Java Streams API. For examples, see the Java performance examples.
There are two operating modes of the GraalVM compiler when used as a HotSpot JIT compiler:
libgraal: the GraalVM compiler is compiled ahead of time into a native shared library. In this operating mode, the shared library is loaded by the HotSpot VM. The compiler uses memory separate from the HotSpot heap and it runs fast from the start since it does not need to warm-up. This is the default and recommended mode of operation.
jargraal: the GraalVM compiler goes through the same warm-up phase that the rest of Java application does. That is, it is first interpreted before its hot methods are compiled. This mode is selected with the -XX:-UseJVMCINativeLibrary
command line option. This will delay the time to reach peak performance as the compiler itself needs to be compiled before it produces code quickly. This mode allows you to debug the GraalVM compiler with a Java debugger.
The options for configuring the GraalVM compiler on the JVM are in 3 categories.
These are general options for setting/getting configuration details.
-XX:-UseJVMCICompiler
: This disables use of the GraalVM compiler as the top tier JIT. This is useful when wanting to compare performance of the GraalVM compiler against the native JIT compilers.-Dgraal.CompilerConfiguration=
: Selects the GraalVM compiler configuration to use. If omitted, the compiler configuration with the highest auto-selection priority is used. To see the set of available configurations, supply the value help to this option.
The current configurations and their semantics are:
enterprise
: To produce highly optimized code with a possible trade-off to compilation time. This value is only available in GraalVM EE.community
: To produce reasonably optimized code with a faster compilation time.economy
: To compile as fast as possible with less optimal throughput of the generated code.-Dgraal.ShowConfiguration=none
: Prints information about the GraalVM compiler configuration selected. This option only produces output when the compiler is initialized. By default, the GraalVM compiler is initialized on the first top-tier compilation. For this reason, the way to use this option is as follows: java -XX:+EagerJVMCI -Dgraal.ShowConfiguration=info -version
.
The accepted values for this option are:
none
: To show no information.info
: To print one line of output showing the name of the compiler configuration in use and the location it is loaded from.verbose
: To print detailed compiler configuration information.-Dgraal.MitigateSpeculativeExecutionAttacks=None
: Selects a strategy to mitigate speculative execution attacks (e.g., SPECTRE).
Accepted values are:
None
: No mitigations are used in JIT compiled code.AllTargets
: All branches are protected against speculative attacks. This has a large performance impact.GuardTargets
: Only branches that preserve Java memory safety are protected. This has reduced performance impact.NonDeoptGuardTargets
: Same as GuardTargets except that branches which deoptimize are not protected since they can not be executed repeatedly.-Dgraal.UsePriorityInlining=true
: This can be used to disable use of the advanced inlining algorithm that favors throughput over compilation speed. This option is only available in GraalVM EE.-Dgraal.Vectorization=true
: This can be used to disable the auto vectorization optimization. This option is only available in GraalVM EE.-Dgraal.OptDuplication=true
: This can be used to disable the path duplication optimization. This option is only available in GraalVM EE.-Dgraal.TraceInlining=false
: Enables tracing of inlining decisions. This can be used for advanced tuning where it may be possible to change the source code of the program. The output format is shown below:
compilation of 'Signature of the compilation root method':
at 'Sig of the root method' ['Bytecode index']: <'Phase'> 'Child method signature': 'Decision made about this callsite'
at 'Signature of the child method' ['Bytecode index']:
|--<'Phase 1'> 'Grandchild method signature': 'First decision made about this callsite'
\--<'Phase 2'> 'Grandchild method signature': 'Second decision made about this callsite'
at 'Signature of the child method' ['Bytecode index']: <'Phase'> 'Another grandchild method signature': 'The only decision made about this callsite.'
For example:
compilation of java.lang.Character.toUpperCaseEx(int):
at java.lang.Character.toUpperCaseEx(Character.java:7138) [bci: 22]:
├── java.lang.CharacterData.of(int): no, bytecode parser did not replace invoke
└── java.lang.CharacterData.of(int): yes, worth inlining according to the cost-benefit analysis.
at java.lang.Character.toUpperCaseEx(Character.java:7138) [bci: 26]:
├── java.lang.CharacterDataLatin1.toUpperCaseEx(int): no, bytecode parser did not replace invoke
└── java.lang.CharacterDataLatin1.toUpperCaseEx(int): yes, worth inlining according to the cost-benefit analysis.
at java.lang.CharacterDataLatin1.toUpperCaseEx(CharacterDataLatin1.java:223) [bci: 4]:
├── java.lang.CharacterDataLatin1.getProperties(int): no, bytecode parser did not replace invoke
└── java.lang.CharacterDataLatin1.getProperties(int): yes, worth inlining according to the cost-benefit analysis.
-Dgraal.CompilationFailureAction=Silent
: Specifies the action to take when compilation fails by throwing an exception.
The accepted values are:
Silent
: Print nothing to the console.Print
: Print a stack trace to the console.Diagnose
: Retry the compilation with extra diagnostics enabled. On VM exit, the collected diagnostics are saved to a zip file that can be submitted along with a bug report. A message is printed to the console describing where the diagnostics file is saved: Graal diagnostic output saved in /Users/graal/graal_dumps/1549459528316/graal_diagnostics_22774.zip
ExitVM
: Same as Diagnose
except that the VM process exits after retrying.For all values except for ExitVM
, the VM continues executing.
-Dgraal.CompilationBailoutAsFailure=false
: The compiler may not complete compilation of a method due to some property or code shape in the method (e.g. exotic uses of the jsr and ret bytecodes). In this case the compilation bails out. If you want to be informed of such bailouts, this option makes GraalVM treat bailouts as failures and thus be subject to the action specified by the -Dgraal.CompilationFailureAction
option.-Dgraal.PrintCompilation=false
: Prints an informational line to the console for each completed compilation. For example: HotSpotCompilation-11 Ljava/lang/Object; wait ()V | 591ms 12B 92B 4371kB
HotSpotCompilation-175 Ljava/lang/String; lastIndexOf (II)I | 590ms 126B 309B 4076kB
HotSpotCompilation-184 Ljava/util/concurrent/ConcurrentHashMap; setTabAt ([Ljava/util/concurrent/ConcurrentHashMap$Node;ILjava/util/concurrent/ConcurrentHashMap$Node;)V | 591ms 38B 67B 3411kB
HotSpotCompilation-136 Lsun/nio/cs/UTF_8$Encoder; encode ([CII[B)I | 591ms 740B 418B 4921
The GraalVM compiler properties above are usable with some other GraalVM launchers such as node
, js
and lli
. The prefix for specifying the properties is slightly different. For example:
$ java -XX:+EagerJVMCI -Dgraal.ShowConfiguration=info -version
Becomes:
$ js --jvm --vm.Dgraal.ShowConfiguration=info -version
Note the -D
prefix is replaced by --vm.D
.
When running the GraalVM compiler on the JVM, it goes through the same warmup phase that the rest of Java application does. That is, it is first interpreted before its hot methods are compiled. This can translate into slightly longer times until the application reaches peak performance when compared to the native compilers in the JVM such as C1 and C2.
To address the issue of taking longer to reach to peak performance, libgraal was introduced – a shared library, produced using Native Image framework to ahead-of-time compile the compiler itself. That means the GraalVM compiler is deployed as a native shared library. In this mode, the compiler uses memory separate from the HotSpot heap and it runs compiled from the start. That is, it has execution properties similar to other native HotSpot compilers such as C1 and C2. Currently, this is the default mode of operation in both GraalVM Community and Enterprise images. It can be disabled with -XX:-UseJVMCINativeLibrary
.
The first thing to be sure of when measuring the performance is to ensure the JVM is using the GraalVM compiler. In the GraalVM binary, the JVM is configured to use the GraalVM compiler as the top tier compiler by default. You can confirm this by adding -Dgraal.ShowConfiguration=info
to the command line. It will produce a line of output similar to the one below when the compiler is initialized:
Using Graal compiler configuration 'community' provided by org.graalvm.compiler.hotspot.CommunityCompilerConfigurationFactory loaded from jar:file:/Users/dsimon/graal/graal/compiler/mxbuild/dists/graal.jar!/org/graalvm/compiler/hotspot/CommunityCompilerConfigurationFactory.class
Note that the GraalVM compiler is only initialized on the first top tier JIT compilation request so if your application is short lived, you may not see this output.
Optimizing JVM-based applications is a science in itself. The compilation may not even be a factor in the case of poor performance as the problem may lie in any other part of the VM (I/O, garbage collection, threading etc) or in poorly written application or 3rd party library code. For this reason, it’s worth using profilers such as Java Mission Control to diagnose application behavior.
You can also compare performance against the native top tier compiler in the JVM by adding -XX:-UseJVMCICompiler
to the command line.
If you observe a significant performance regression when using the GraalVM compiler, please open an issue on GitHub. Attaching a Java Flight Recorder log and instructions to reproduce the issue makes investigation easier and thus chances of a fix higher. Even better is if you can submit a JMH benchmark that represents the hottest parts of your application (as identified by a profiler). This allows us to very quickly pinpoint missing optimization opportunities or to offer suggestions on how to restructure the code to avoid or reduce performance bottlenecks.
Like all software, the GraalVM compiler is not guaranteed to be bug free so it is useful to know how to diagnose and submit useful bug reports if you encounter issues.
If you spot a security vulnerability, please do not report it via GitHub Issues or the public mailing lists, but via the process outlined at Reporting Vulnerabilities guide.
One advantage of the compiler being written in Java is that runtime exceptions during compilation are not fatal VM errors. Instead, each compilation has an exception handler that takes an action based on the graal.CompilationFailureAction
property.
The default value is Silent
. Specifying Diagnose
causes failing compilations to be retried with extra diagnostics enabled. Just before the VM exits, all diagnostic output captured during retried compilations is written to a .zip
file and its location is printed on the console:
Graal diagnostic output saved in /Users/demo/graal-dumps/1499768882600/graal_diagnostics_64565.zip
You can then attach the .zip file to an issue on GitHub.
Apart from Silent
and Diagnose
the following values for graal.CompilationFailureAction
are supported:
Print
: Prints a message and stack trace to the console but does not do the re-compilation.ExitVM
: Same as Diagnose
but the VM process exits after the re-compilation.The other type of error compilers can have is producing incorrect machine code. This error can cause a VM crash, which should produce a file that starts with hs_err_pid
in the current working directory of the VM process. In most cases, there is a section in the file that shows the stack at the time of the crash, including the type of code for each frame in the stack, as in the following example:
Stack: [0x00007000020b1000,0x00007000021b1000], sp=0x00007000021af7a0, free space=1017k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
J 761 JVMCI org.graalvm.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V (299 bytes) @ 0x0000000108a2fc01 [0x0000000108a2fac0+0x141] (null)
j org.graalvm.compiler.core.gen.NodeLIRBuilder.doBlock(Lorg/graalvm/compiler/nodes/cfg/Block;Lorg/graalvm/compiler/nodes/StructuredGraph;Lorg/graalvm/compiler/core/common/cfg/BlockMap;)V+211
j org.graalvm.compiler.core.LIRGenerationPhase.emitBlock(Lorg/graalvm/compiler/nodes/spi/NodeLIRBuilderTool;Lorg/graalvm/compiler/lir/gen/LIRGenerationResult;Lorg/graalvm/compiler/nodes/cfg/Block;Lorg/graalvm/compiler/nodes/StructuredGraph;Lorg/graalvm/compiler/core/common/cfg/BlockMap;)V+65
This example shows that the top frame was compiled (J) by the JVMCI compiler, which is the GraalVM compiler. The crash occurred at offset 0x141 in the machine code produced for:
org.graalvm.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V
The next two frames in the stack were executing in the interpreter (j). The location of the crash is also often indicated near the top of the file with something like this:
# Problematic frame:
# J 761 JVMCI org.graalvm.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V (299 bytes) @ 0x0000000108a2fc01 [0x0000000108a2fac0+0x141] (null)
In this example, there is likely an error in the code produced by the GraalVM compiler for NodeLIRBuilder.matchComplexExpressions
.
When filing an issue on GitHub for such a crash, you should first attempt to reproduce the crash with extra diagnostics enabled for the compilation of the problematic method. In this example, you would add the following to your command line:
-Dgraal.MethodFilter=NodeLIRBuilder.matchComplexExpressions, -Dgraal.Dump=:2
These options are described in more detail here. In brief, these options tell the compiler to capture snapshots of the compiler state at verbosity level 2 while compiling any method named matchComplexExpressions
in a class with a simple name of NodeLIRBuilder
. The complete format of the MethodFilter
option is described in the output of java -XX:+JVMCIPrintProperties
.
Quite often, the crash location does not exist directly in the problematic method mentioned in the crash log but comes from an inlined method.
In such a case, simply filtering for the problematic method might not capture an erroneous compilation causing a crash.
To improve the likelihood of capturing an erroneous compilation, you need to broaden the MethodFilter
value. To guide this, add -Dgraal.PrintCompilation=true
when trying to reproduce the crash so you can see what was compiled just before the crash.
The following shows sample output from the console:
HotSpotCompilation-1218 Lorg/graalvm/compiler/core/amd64/AMD64NodeLIRBuilder; peephole (Lorg/graalvm/compiler/nodes/ValueNode;)Z | 87ms 428B 447B 1834kB
HotSpotCompilation-1212 Lorg/graalvm/compiler/lir/LIRInstructionClass; forEachState (Lorg/graalvm/compiler/lir/LIRInstruction;Lorg/graalvm/compiler/lir/InstructionValueProcedure;)V | 359ms 92B 309B 6609kB
HotSpotCompilation-1221 Lorg/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator; getResult ()Lorg/graalvm/compiler/hotspot/HotSpotLIRGenerationResult; | 54ms 18B 142B 1025kB
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x000000010a6cafb1, pid=89745, tid=0x0000000000004b03
#
# JRE version: OpenJDK Runtime Environment (8.0_121-b13) (build 1.8.0_121-graalvm-olabs-b13)
# Java VM: OpenJDK 64-Bit GraalVM (25.71-b01-internal-jvmci-0.30 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# J 1221 JVMCI org.graalvm.compiler.hotspot.amd64.AMD64HotSpotLIRGenerator.getResult()Lorg/graalvm/compiler/hotspot/HotSpotLIRGenerationResult; (18 bytes) @ 0x000000010a6cafb1 [0x000000010a6caf60+0x51] (null)
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
Here we see that the crash happened in a different method than the first crash. As such, we expand the filter argument to be -Dgraal.MethodFilter= NodeLIRBuilder.matchComplexExpressions,AMD64HotSpotLIRGenerator.getResult
and run again.
When the VM crashes in this way, it does not execute the shutdown code that archives the GraalVM compiler diagnostic output or delete the directory it was written to. This must be done manually after the crash.
By default, the directory is $PWD/graal-dumps/
; for example, ./graal-dumps/1499938817387
. However, you can set the directory with -Dgraal.DumpPath=
.
A message, such as the following, is printed to the console when this directory is first used by the compiler:
Dumping debug output in /Users/demo/graal-dumps/1499768882600
This directory should contain content related to the crashing method, such as:
$ ls -l /Users/demo/graal-dumps/1499768882600
-rw-r--r-- 1 demo staff 144384 Jul 13 11:46 HotSpotCompilation-1162[AMD64HotSpotLIRGenerator.getResult()].bgv
-rw-r--r-- 1 demo staff 96925 Jul 13 11:46 HotSpotCompilation-1162[AMD64HotSpotLIRGenerator.getResult()].cfg
-rw-r--r-- 1 demo staff 12600725 Jul 13 11:46 HotSpotCompilation-791[NodeLIRBuilder.matchComplexExpressions(List)].bgv
-rw-r--r-- 1 demo staff 1727409 Jul 13 11:46 HotSpotCompilation-791[NodeLIRBuilder.matchComplexExpressions(List)].cfg
https://www.graalvm.org/