JVM

illustrations illustrations illustrations illustrations illustrations illustrations illustrations

JVM

Published on Oct 10, 2019 by Malys

Compare different JVM to improve container performance.

State of play

We will focus on OpenJDK because it’s the reference for JDK, it’s an open project and many companies are contributors (Oracle, RedHat, …).

In our case, we will be based on AdoptOpenJDK distributions.

Given that some difference could be found in OpenJDK packaging (RedHat, Zulu, ..), our tests will be based on AdoptOpenJDK packaging.

This page provides OpenJDK distributions for Linux and Windows platforms. It could be easier to compare results with the same base.

OpenJDK HotSpot

OpenJDK (Open Java Development Kit) is a free and open-source implementation of the Java Platform, Standard Edition (Java SE).

OpenJDK 11 provides new interesting features:

  • Class data sharing or CDS

    Class data sharing (CDS) helps reduce the startup time for Java programming language applications, in particular smaller applications, as well as reduce footprint. When the JRE is installed using the installer, the installer loads a set of classes from the system jar file into a private internal representation, and dumps that representation to a file, called a “shared archive”. If the JRE installer is not being used, this can be done manually, as explained below. During subsequent JVM invocations, the shared archive is memory-mapped in, saving the cost of loading those classes and allowing much of the JVM’s metadata for these classes to be shared among multiple JVM processes.

  • Java Platform Module System (Jigsaw)

    The Java Platform Module System[1] specifies a distribution format for collections of Java code and associated resources. It also specifies a repository for storing these collections, or modules, and identifies how they can be discovered, loaded and checked for integrity. It includes features such as namespaces with the aim of fixing some of the shortcomings in the existing JAR format, especially the JAR Hell, which can lead to issues such as classpath and class loading problems.

  • Low GC pause by default: G1

Container friendly

Before OpenJDK 8u131, JRE embedded in a container didn’t detect allocated resources to a container but it managed host resources. Sometimes, system killed Java process because it passed allocated resources limits.

The solution to detect container resources was developed for JDK 9 but it was retro-featured in 8u131 as an option. Since 8u391, was enabled by default. You can disable it using this parameter -XX:-UseContainerSupport. Obviously, OpenJDK 11 integrates this feature.

Eclipse OpenJ9 HotSpot

Eclipse OpenJ9 is a high performance, scalable, Java virtual machine implementation that is fully compliant with the Java Virtual Machine Specification.

OpenJ9 provides new interesting features:

  • JDK 8-11 supports
  • Container friendly by default
  • Class Data Sharing
  • Dynamic Ahead-Of-Time
  • Startup mode (quick, virtualized)

GraalVM by Oracle

GraalVM is a universal virtual machine for running applications written in JavaScript, Python, Ruby, R, JVM-based languages like Java, Scala, Groovy, Kotlin, Clojure, and LLVM-based languages such as C and C++. It can be considered like the next generation of HotSport JVM.

GraalVM provides new interesting features:

  • Full JVM Platform
  • Polyglot : Python, Java, Rust, NodeJS over GraalVM
  • Graal compiler: full Java compiler, successor of C2 compiler for long time high performance
  • Community edition
  • Enterprise edition
    • optimization
    • debugging
  • Native image generation

Native compilation

To improve performance on runtime, GraalVM will processing and apply optimization during compilation phase. This mechanism called Ahead Of Time Compilation provides a static executable of your application corresponding to a snapshot:

  • embedded minimal dependencies VM: Substract VM
  • optimized application

It’s possible to apply this process to an library or a full Java application. Static executable is self-sufficient with high performance.

LTS / Production Ready

For our usage, we want stability and production ready JRE that’s why we will use only LTS of JRE. OpenJ9 follows the same lifetime of OpenJDK. Currently, GraalVM supports only JDK 8 but 11’s support is in progress.

JDK 1st release last release
OpenJDK 8 Q3 2023
OpenJDK 11 Q4 2024
OpenJDK 17 Q3 2021
GraalVM Q2 2019

Performance comparative

Method

Tests are based on:

  • different use cases:

    • Stream only: new Java 8 feature
    • Microprofile: new Jakarta standard
    • SpringBoot: standard framework
    • RH-SSO 7.3: AppServer compatible JDK 8 & 11
  • metrics (two automatic measures)

    • Start time o process time
    • Memory: RAM usage
    • Latency (mean, 50%, 90%): closed test by thread
    • Throughput: efficiency

Comparative

Java Streams
Jakarta Microprofile
SpringBoot
RH-SSO 7.3
Results

Currently, OpenJDK 11 is the most polyvalent JRE and provides good performance in most of cases. We recommend to use it by default.

OpenJ9 could be used for high memory use case because it’ s very efficient to minimize memory footprint. As soon as possible,OpenJDK8 should be replaced by OpenJDK11.

GraalVM as JRE is too young and currently, it’s not enough efficient.

Some public posts confirms my results and my conclusion. (1, 2, 3,…)

Java Platform Module System

Modularity can be applied to application with dependencies but also to JRE distribution.

Custom JRE

It’s possible to generate a custom and minimal JRE to cover only JRE application needs.

# JDK11
sdk use java 11.0.4.j9-adpt && du -hcs  $JAVA_HOME
# 300M    /java/11.0.4.j9-adpt
# Compile
javac CountUppercase.java
# Deps
jdeps --print-module-deps CountUppercase.class
# Generate distribution
jlink --no-header-files --no-man-pages --compress=2 --strip-debug \
 --add-modules  $(jdeps --print-module-deps CountUppercase.class) \
 --output java-base
# Custom distribution
ls java-base/ && du -hcs
# bin  conf  legal  lib  release -> java-base/ 39M
JAVA_HOME=$PWD/java-base

In the case of an hello world with no dependency, the custom distribution is 30MB instead of 116MB for a full JRE.