X

Oracle GraalVM

  • June 2, 2020

Accelerating OCI applications with GraalVM Enterprise

Olga Gupalo
Member of Technical Staff

Oracle GraalVM Enterprise Edition is a high performance runtime that provides significant improvements in application execution and efficiency on premise and in the cloud.  For cloud, the good news is that GraalVM Enterprise is included in all Oracle Cloud Infrastructure (OCI) subscriptions so you can use it for no additional charge.  Let's see how to get started with GraalVM Enterprise in OCI and run some examples in a virtual machine. 

While GraalVM Enterprise's improved performance and reduced resource requirements can lead to cost savings for OCI customers, OCI itself is running services on GraalVM Enterprise.  For example, the Oracle Cloud Infrastructure Monitoring service, which captures and provides analysis of service health and performance metrics, has reported a 5% reduction in CPU consumption and a 10% increase in the number of transactions per second when using GraalVM Enterprise.  For OCI user workloads, GraalVM Enterprise can be used in the following deployment scenarios:

Each of those deserves separate in-depth coverage, but for now we will focus on the first scenario, using GraalVM Enterprise in an OCI virtual machine compute instance.  We'll start by installing GraalVM Enterprise into our virtual machine and then run some samples to illustrate usage. First, we'll build and deploy the well known Spring PetClinic web application and make it accessible on the public internet.  Then we will go over a JMH benchmark to measure the performance of a Java application using the GraalVM compiler versus the JDK's default C2 compiler.  Finally, we will build and run a Micronaut microservice example, and then we'll compile it using GraalVM's native image ahead-of-time compiler to generate a native Linux executable.

In an OCI VM there are two ways to install GraalVM Enterprise:

  • Use YUM to install the necessary Linux packages
  • Download and install GraalVM Enterprise manually

We'll use YUM as this is the easiest and recommended approach.  For manual installation, consult the instructions in the reference guide.  If you have an OCI VM ready you can use that or create a new one.  If you need to learn how to create a VM you can work through the first Linux instance tutorial.  We'll use a VM.Standard.E3.Flex with the Oracle Linux 7.8 pre-built image that we'll call demo-instance.

You can connect to an instance using a Secure Shell (SSH) or other remote desktop service.  To use SSH on a Unix-style system you would execute a command like the following, substituting in your VM's public IP address:

ssh -i ~/.ssh/id_rsa opc@INSTANCE_PUBLIC_IP
For example,
ssh -i ~/.ssh/id_rsa opc@150.136.138.23

The key file reference ~/.ssh/id_rsa is the name of the file containing the private SSH key associated with the public key used during the instance creation, opc is the default user name for the Oracle Linux image, and 150.136.138.23 is the public IP address of the demo instance. For more details, refer to the Connecting to Your Linux Instance Using SSH guide.

Oracle GraalVM Enterprise Edition RPMs are available in the OCI YUM repository, which means that OCI users can install GraalVM Enterprise using yum — a package-management utility for the Linux operating systems. Once you are ssh'd into your instance, verify what GraalVM RPMs are available for installation. 

First refresh the repo data and search for available GraalVM Enterprise packages:

$ yum check-update 
$ sudo yum provides graalvm*

The resulting list is huge as it includes both current and previous versions of all of the available components, but we are looking for just the latest "Oracle GraalVM Enterprise Edition JDK8 Java Development Kit". To install GraalVM Enterprise version 20.2 with JDK 8 run the following command:

$ sudo yum install graalvm20-ee-8-jdk-20.2.0-1.el7.x86_64

After installation, the GraalVM Enterprise binaries will be stored in /usr/lib64/graalvm.  You can verify the installation by checking the Java version:

$ java -version
java version "1.8.0_261"
Java(TM) SE Runtime Environment (build 1.8.0_261-b33)
Java HotSpot(TM) 64-Bit Server VM GraalVM EE 20.2.0 (build 25.261-b33-jvmci-20.2-b03, mixed mode)

Let's set JAVA_HOME and point the PATH variable to the GraalVM Enterprise install's bin folder in the bash configuration with the following commands:

$ echo "export JAVA_HOME=/usr/lib64/graalvm/graalvm20-ee-java8" >> ~/.bashrc
$ echo "export PATH='$JAVA_HOME'/bin:'$PATH' " >> ~/.bashrc

Once you run the following command to get the change into the current SSH session we'll be ready to start:

$ source ~/.bashrc

Time to run some examples!

Spring PetClinic

GraalVM Enterprise runs Java applications out of the box on either a local or virtual host.  We are going to demonstrate that using the well-known Spring PetClinic sample application.  With a few commands we can download the PetClinic source, build it, and get it running:

$ wget -O spring-petclinic.zip https://github.com/spring-projects/spring-petclinic/archive/main.zip
$ unzip spring-petclinic.zip
$ cd spring-petclinic-main
$ ./mvnw package 
$ java -jar target/*.jar

The Maven build pulls down many packages so it can take a couple of minutes.

To visit PetClinic from your desktop browser we must ensure that the Linux host firewall and OCI security lists allow traffic to the virtual machine's port 8080.  Each compute instance in OCI is attached to a Virtual Cloud Network (VCN), which is comprised of a collection of subnets that can be configured to enable network connections to an instance using custom ingress rules.  In the OCI web console, navigate to your VCN, then to the Public Subnet connected to the instance, and open its Default Security List. Press "Add Ingress Rule" and fill in the following data:  

Make sure the destination port is 8080.  Once you've created the ingress rule, restart the firewall in your running instance:

$ sudo firewall-cmd --permanent --add-port=8080/tcp
$ sudo systemctl reload firewalld

You should be able to connect to the PetClinic running on OCI using the public IP of the instance and port 8080, for example,  http://150.136.138.23:8080.

As long as your firewall and VCN settings permit network access it is fairly easy to deploy a Spring Boot or any other server-side application using GraalVM Enterprise!

JMH Performance Benchmark

The second example we are going to run is a JMH benchmark application to assess its performance using the GraalVM Enterprise compiler and the JDK C2 compiler. The benchmark creates a stream from array elements and maps each number using several mapping functions. GraalVM Enterprise achieves performance improvements of between 2x and 5x on Java Stream programs.

First, download the GraalVM examples and unzip them:

$ wget -O graalvm-demos.zip https://codeload.github.com/graalvm/graalvm-demos/zip/master
$ unzip graalvm-demos.zip

Once unzipped, cd into the graalvm-demos-master/java-simple-stream-benchmark directory:

$ cd graalvm-demos-master/java-simple-stream-benchmark

The benchmark project is built with Maven. Unlike the previous PetClinic Spring application, this demo does not come with a Maven Wrapper (.mvnw) and Oracle Linux 7 does not include Maven by default, so we will need to install it using yum:

sudo yum install rh-maven35

Once installed we need to add a Maven script to your .bashrc:

$ echo "source /opt/rh/rh-maven35/enable" >> ~/.bashrc
$ source ~/.bashrc

With Maven installed and configured, we can build and run the benchmark with the GraalVM Enterprise JIT compiler:

$ mvn package
$ java -jar target/benchmarks.jar

The JavaSimpleStreamBenchmark.testMethod is executed with 3 iterations to allow the JIT compiler to warmup before it samples the performance. The benchmark result is in nanoseconds per operation which means lower numbers are better. 

Now let's run this benchmark on the same JVM (GraalVM Enterprise), but without the GraalVM compiler by adding the -XX:-UseJVMCICompiler option. With this option, the JVM will use the C2 compiler that is default in most JDKs:

$ java -XX:-UseJVMCICompiler -jar target/benchmarks.jar 

The difference in score results is just striking!  The average number of nanoseconds per operation drops from  ~370.000 with C2  to ~10.000 with GraalVM Enterprise — 30x faster! Your performance measurement should be similar.

Micronaut Microservices

For our final example we are going to run a Micronaut application on GraalVM Enterprise with the JIT compiler and then as compile that same Java application into a native executable.  Download example sources and unzip:

$ wget -O micronaut-creating-first-graal-app-master.zip https://codeload.github.com/micronaut-guides/micronaut-creating-first-graal-app/zip/master
$ unzip micronaut-creating-first-graal-app-master.zip

Change to the micronaut-creating-first-graal-app-master directory, build and run the example with Gradle Wrapper in a JVM mode:

$ cd micronaut-creating-first-graal-app-master
$ ./gradlew assemble
$ ./gradlew run

The application is started on port 8080. The Micronaut @Controller annotation is mapped to the /conferences path so to obtain a random conference name we can do a GET on http://<public_IP_address>:8080/conferences/random, for example:

http://150.136.138.23:8080/conferences/random

Now let's terminate the JVM and generate a native Linux executable with GraalVM Enterprise Native Image.  By design, the Micronaut framework does not rely on reflection or dynamic class loading so it works well with GraalVM Enterprise Native Image. Native Image comes as a separate installable so we need to add it to the base installation. Search for a proper package name with yum and then install it:

$ yum search native-image

Again you'll see various available versions. But we're using GraalVM Enterprise 20 with JDK 8 support so let's install the corresponding Native Image package and compile our application ahead-of-time:

$ sudo yum install graalvm20-ee-8-native-image.x86_64
$ native-image --no-fallback --no-server -cp complete/build/libs/complete-0.1-all.jar example.micronaut.Application $ ./micronaut-graal-app

Native image compilation times depend on application size and complexity and may take some time on low powered VMs.

Let's compare startup time when running the Micronaut application on the JVM and as a native image:

It starts 60x faster! The executable is a self-contained binary and does not require a JDK to run which makes it an easy way to distribute applications.  It's also quite small at only 60MB.

Conclusion

High performance, fast startup, and lower CPU and memory consumption make GraalVM Enterprise the perfect platform for cloud deployed Java applications.  Many of the Oracle Cloud Infrastructure services you're using are powered by GraalVM Enterprise, and you can take advantage of it too at no additional cost — it is available free of charge to all OCI users to improve the performance of their workloads and reduce their cloud resource consumption.

If you would like to try GraalVM Enterprise on OCI but do not have an account, you can sign up for the Oracle Cloud Free Tier program.