Run JVM-based Languages with GraalVM Enterprise

By definition, GraalVM Enterprise is a virtual machine and one of its function is to run JVM-based applications. GraalVM Enterprise can provide benefits by ensuring the extensibility via scripting languages. A novel just-in-time compilation technology of GraalVM can make Java applications perform faster. The compiler of GraalVM assures performance advantages for highly abstracted programs due to its ability to remove costly object allocations.

Disclaimer: Oracle GraalVM Enterprise Edition is developed on top of Oracle JDK. The documentation provided here contains only material that makes GraalVM Enterprise distinctive from the base. The basic syntax to launch a Java application remains the same:

java [options] class [arguments]

To execute a JAR file containing class and resource files for the application, specify -jar option followed by a JAR file name to be called:

java [options] -jar file.jar [arguments]

Options are command-line launcher options, when arguments after the class name or JAR file name are passed to the main function. By default, if the first argument is not an option, java command treats it as a class file name. The java command started from the command line takes arguments to control the application behaviour. All java -help options are inherited from Java HotSpot, including the -XX options. See the latter with java -XX:+PrintFlagsFinal.

By running the java command included in GraalVM, you will by definition use the GraalVM just-in-time compiler – no extra configuration is needed.

Compiler Configuration Options

The GraalVM compiler configuration options can be used for configuring general settings, tuning performance or diagnosing problems.

There are two operating modes of the GraalVM compiler when used as a HotSpot JIT compiler:

To determine the difference between running the GraalVM compiler in a Native Image versus on the JVM, measure performance and troubleshoot the compiler, proceed to the JVM Operations Manual below.

General Options

Since the GraalVM compiler is an instantiation of a JVMCI compiler, there is a number of options for configuring JVMCI specific functionality:

Option and Default Value Description
-XX:+UseJVMCICompiler Disable the use of the GraalVM compiler as the top tier JIT compiler. This is useful when wanting to compare performance of the GraalVM compiler against the native JIT compilers.
-Dgraal.CompilerConfiguration=name Select 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 produces highly optimized code with a possible trade-off to compilation time. This value is only available in GraalVM Enterprise. community produces reasonably optimized code with a faster compilation time. economy compiles as fast as possible with less optimal throughput of the generated code.
-Dgraal.ShowConfiguration=none Print information about the GraalVM compiler configuration selected. The accepted values for this option are: none - shows no information. info - prints one line of output showing the name of the compiler configuration in use and the location it is loaded from.verbose - prints detailed compiler configuration information. This option only produces output when the GraalVM compiler is initialized. By default, the GraalVM compiler is initialized on the first top-tier compilation. For this reason, use this option as follows: java -XX:+EagerJVMCI -Dgraal.ShowConfiguration=info -version
-Dgraal.MitigateSpeculativeExecutionAttacks=None Select 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.

Performance Tuning Options

Option and Default Value Description
-Dgraal.OptDuplication=true This can be used to disable the path duplication optimization. This option is only available in GraalVM Enterprise.
-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 Enterprise.
-Dgraal.Vectorization=true This can be used to disable the auto vectorization optimization. This option is only available in GraalVM Enterprise.
-Dgraal.TraceInlining=false Enable tracing of inlining decisions. This can be used for advanced tuning where it may be possible to change the source code of the program. For example: compilation of java.lang.Character.toUpperCaseEx(int): at java.lang.Character.toUpperCaseEx(Character.java:7138) [bci: 22]: ├──<GraphBuilderPhase> java.lang.CharacterData.of(int): no, bytecode parser did not replace invoke └──<PriorityInliningPhase> java.lang.CharacterData.of(int): yes, worth inlining according to the cost-benefit analysis. at java.lang.Character.toUpperCaseEx(Character.java:7138) [bci: 26]

Diagnostic Options

Option and Default Value Description
-Dgraal.CompilationFailureAction=Silent Specify the action to take when compilation fails by throwing an exception. The accepted values are: Silent - prints nothing to the console. Print - prints a stack trace to the console. Diagnose - retries 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 treats this differently and bails out. If you want to be informed of such bailouts, this option makes the GraalVM compiler treats bailouts as failure and thus be subject to the action specified by the -Dgraal.CompilationFailureAction option.
-Dgraal.PrintCompilation=false Print 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

Interoperability

In addition to running JVM-based languages, you can also call any other language implemented with Truffle Language Implementation framework directly from Java. See the Write Polyglot Programs and the Embed Languages for more information about interoperability with other programming languages.

JVM Operations Manual

Difference between running the GraalVM compiler in a Native Image vs on the JVM

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 GraalVM Enterprise Native Image 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.

Measuring Performance

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.

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.

Troubleshooting the GraalVM Compiler

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.

Compilation Exceptions

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:

Code Generation Errors

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 you see that the crash happened in a different method than the first crash. As such, 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/<timestamp>; for example, ./graal-dumps/1499938817387. However, you can set the directory with -Dgraal.DumpPath=<path>.

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

You should attach a zip of this directory to an issue on GitHub.