Baeldung

Java, Spring and Web Development tutorials

 

Guide to Eclipse OpenJ9 JVM
2025-04-24 20:57 UTC by Kostiantyn Ivanov

start here featured

1. Overview

OpenJ9 is a high-performance, scalable, and flexible Java Virtual Machine. We can use it as a replacement for HotSpot in many Java applications. IBM originally developed it as part of its commercial JDK. Now this JVM is supported by the Eclipse Foundation.

We use OpenJ9 to reduce memory footprint and startup time, especially in cloud and container environments. It supports the Java SE specification and works well with OpenJDK.  In this article, we’ll explore how to install OpenJ9 JVM and review its main features.

2. Installation

We can install OpenJ9 by downloading a prebuilt OpenJDK binary that includes it. Many distributions, like Eclipse Temurin, offer builds with OpenJ9. The official release list is on the Eclipse OpenJ9 website.

After downloading the archive, we extract the files and set the JAVA_HOME environment variable.

2.1. Windows

First, we extract the ZIP archive to a preferred location, such as path-to-jdk\jdk-openj9. Then we set the JAVA_HOME environment variable and modify the PATH environment variable:

$ setx JAVA_HOME "path-to-jdk\jdk-openj9" 
$ setx PATH "%JAVA_HOME%\bin;%PATH%"

Finally, we verify the installation:

$ java -version

2.2. Linux

First, let’s extract the archive:

$ tar -xzf OpenJ9-JDK.tar.gz -C /opt

Then we set JAVA_HOME and update the PATH:

$ export JAVA_HOME=/opt/jdk-openj9 
$ export PATH=$JAVA_HOME/bin:$PATH

We can add these lines to .bashrc, or .zshrc to make them permanent. Finally, we check the installation:

$ java -version

Now we’re ready to use OpenJ9 for running and developing Java applications.

3. Garbage Collection Policies

OpenJ9 offers multiple garbage collection policies. Each policy is optimized for different workloads. We can choose the policy that fits our performance goals.

In the HotSpot JVM, we configure garbage collection by selecting a GC implementation and tuning many parameters. In OpenJ9, we use GC policies instead. Each policy is designed for a specific use case, and we choose one at startup using the virtual machine option:

-Xgcpolicy:<name>

Let’s look at the available GC policies in OpenJ9.

3.1. Generational Concurrent GC Policy

This is the default policy and we’ll use it for most applications. Using this GC policy, we split the heap into two areas: nursery and tenure. This policy manages two generation groups: new and older.

It performs global GC using concurrent mark-sweep, optionally followed by compaction. Partial GC is a stop-the-world scavenge or concurrent scavenge.

We enable it with the following flag:

-Xgcpolicy:gencon

3.2. Balanced Policy

We use this policy for large heaps and many threads when we face unacceptable pause times with gencon. This policy divides the heap into equal-sized regions and obtains the support of multiple generations.

Global GC uses incremental concurrent marking. Partial GC uses a stop-the-world and optional mark, sweep, or compaction.

We can add this policy using the flag:

-Xgcpolicy:balanced

3.3. Optimize for Pause Time Policy

We choose this policy to minimize pause times. For example, it may be useful when we have a large heap size and it affects the length of the GC pause. This policy uses a single, flat heap area. Here we have only one generation.

GC is concurrent mark-sweep with optional compaction.

To start using this policy we have to add the following flag:

-Xgcpolicy:optavgpause

3.4. Optimize for Throughput Policy

We select this policy when we want maximum throughput in short-lived applications. We have a flat heap area here. This policy supports one generation.

GC is a stop-the-world mark-sweep with optional compaction.

We can enable this policy using the following flag:

-Xgcpolicy:optthruput

3.5. Metronome Policy

We use this policy for real-time and soft real-time applications. It splits the heap into size-classed regions. Here we have the support of one generation.

The metronome GC runs in short interruptible bursts to avoid long stop-the-world pauses.

To use the metronome policy, we can turn on the flag:

-Xgcpolicy:metronome

3.6. nogc Policy

We use this policy for testing or special use cases where memory isn’t reclaimed. We have a flat heap here. No GC is performed.

We can enable it using the following flag:

-Xgcpolicy:nogc

4. Class Data Sharing and AOT Compilation

OpenJ9 supports Class Data Sharing and Ahead-of-Time compilation out of the box. We can use these features to improve startup time and reduce memory usage.

4.1. Class Data Sharing

Class Data Sharing allows us to cache class metadata in a shared memory cache. We create a shared class cache once and reuse it across JVM runs. This speeds up class loading and reduces memory footprint, especially in container and microservice environments.

To enable CDS, we start the JVM with the following flag:

-Xshareclasses

We can also name the cache and control its size:

-Xshareclasses:name=myCache,cacheDir=/path/to/cache,cacheSize=100M

To view cache statistics, we use:

-Xshareclasses:cacheStats

4.2. Ahead-of-Time Compilation

Ahead-of-time compilation in OpenJ9 compiles Java bytecode into native code before runtime. This reduces JIT warm-up time and improves performance on startup.

Unlike HotSpot, which requires GraalVM for AOT, OpenJ9 has built-in support.  The VM automatically chooses which methods should be AOT-compiled based, but we can disable it using the following flag:

-Xnoaot

5. Diagnostic Data and Tooling

OpenJ9 supports JMX, just like HotSpot. We can connect existing monitoring tools to it.

Additionally, OpenJ9 provides other tools that we can use to troubleshoot performance issues, crashes, or memory problems. We can use dump agents to generate heap dumps, system dumps, and general JVM  information dumps.

To monitor CPU, memory, GC, and thread activity in real time, we could utilize the IBM Health Center.

6. Conclusion

In this tutorial, we saw how OpenJ9 can be a great choice when we want to reduce memory usage and speed up startup time. It works well for cloud workloads, microservices, and containerized apps. However, before using OpenJ9 in production, we should test it with our specific workload.

The same advice applies to GC policy selection. Each policy targets a different use case. We need to measure and compare to choose the best one.

With the right setup, OpenJ9 can deliver solid performance and save resources.

The post Guide to Eclipse OpenJ9 JVM first appeared on Baeldung.
       

 

Content mobilized by FeedBlitz RSS Services, the premium FeedBurner alternative.