Fast application startup with OpenJDK CRaC

This post introduces OpenJDK with the Coordinated Restore at Checkpoint (CRaC) capability. It also helps interested readers quickly try the OpenJDK CRaC functionality, and experience a performance gain, using the Spring Boot PetClinic application.

Background

Java applications are characterized by a slow-startup due to the very design of the Java platform. They are also characterized by a ‘warm-up’ phase where the JVM tries to fire on all cylinders to ensure good performance of the running application. After the warm-up phase, the application may reach a state of peak performance depending on its execution context.

Could we record this peak-performance state and replay it later, so as to have subsequent instances of the Java application start in this very state?

Tools like CRIU(checkpoint/restore in userspace) let us do this. The OpenJDK CRaC project is a Java-effort in the same direction . Co-ordinated Restore at Checkpoint - as the name suggests, augments the JVM with a co-ordination mechanism on top of the checkpoint and restore semantics offered by tools like CRIU. This lets the Java runtime snapshot the state of a Java application on a checkpoint event and reproduce it on a restore event, while customizing actions on these events.

OpenJDK CRaC for Ubuntu

CRaC began as an incubation project under the OpenJDK umbrella. It has achieved production-grade maturity. Upstream efforts are still underway to make CRaC agnostic to the underlying, operating system specific, checkpoint/restore mechanism.

The OpenJDK CRaC packages for versions 17 and 21 were introduced in Ubuntu 24.10, along with an extension of CRIU tailored for CRaC.

To install OpenJDK CRaC on Ubuntu 24.10 and above, use:

apt update && apt install openjdk-21-crac-jdk-headless

Checkpoint and restore of Spring Boot applications

Spring Boot 3.2 released support for CRaC. This coupled with the OpenJDK CRaC runtime lets us checkpoint and restore Spring Boot applications.

The following sections demonstrate building, checkpoint-ing and restoring a Spring Boot application.

Building a Spring Boot application for CRaC

Note: OpenJDK CRaC currently works only on the Ubuntu development release.

Please create a lxc container using the following commands:

lxc launch ubuntu-daily:questing

This should start a Questing Quokka container and return the name of it. Access the shell of this container using:

lxc exec <name> -- /bin/bash

Before you go through the steps below, make sure you have openjdk-21-crac-jdk-headless installed in the container.

Step 1

Clone the Spring Boot PetClinic application

git clone https://github.com/spring-projects/spring-petclinic && \
cd spring-petclinic

Step 2

Add the org.crac dependency into the pom.xml file

<dependency>
    <groupId>org.crac</groupId>
    <artifactId>crac</artifactId>
    <version>1.4.0</version>
</dependency>

Step 3

Point JAVA_HOME to the OpenJDK CRaC installation:

export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-crac-amd64

Step 4

Build with maven

mvn clean package

If the build passes, you should find a file named spring-petclinic-3.5.0-SNAPSHOT.jar under the target directory.

Checkpointing a Spring Boot application

Step 1

Start the application using this command:

$JAVA_HOME/bin/java -XX:CRaCCheckpointTo=$HOME/cr-image -jar target/spring-petclinic-3.5.0-SNAPSHOT.jar

The -XX:CRaCCheckpointTo option lets you configure the directory where the snapshot image will be dumped.

The PetClinic application takes a few seconds to come up. During a run on my setup it took ~5.49 seconds:

The web-app should now be accessible at http://localhost:8080

Step 2

Open another terminal and issue the jcmd command to list the running Java processes. I see this output in my setup:

$ jcmd
4146 target/spring-petclinic-3.5.0-SNAPSHOT.jar
4206 jdk.jcmd/sun.tools.jcmd.JCmd

The PetClinic application is running in process with PID 4146. Let us checkpoint it.

Step 3

To checkpoint, again use jcmd and issue this command:

jcmd 4146 JDK.checkpoint

The PetClinic application should now crash with only a “Killed” message. If you see any other error message, the checkpoint has evidently failed.

And you must find the snapshot data in the $HOME/cr-image directory. It is a set of .img files. Here is a snapshot for reference:

Restoring the checkpointed Spring Boot application

Using the snapshot produced at checkpoint, we attempt to restore the application in the same state.

Use this command to restore:

$JAVA_HOME/bin/java -XX:CRaCRestoreFrom=$HOME/cr-image

The PetClinic app should now be up and running in less than half a second. On my setup it took 418 ms for the first restore run:

Practical considerations

OpenJDK CRaC is well-suited for achieving fast application startup for long-running Java applications that also tend to be stateful in nature. It may not suit short-lived applications, where GraalVM native image clearly performs better.

Ideally, the checkpoint process should be done in a staging environment where a real-world load could be simulated. Subsequently, the checkpoint snapshot may be used in production to bring up application instances instantaneously.

CRaC also presents a Java API that lets users carefully selected classes as “CRaC Resources”. Users may then define callbacks on these classes which are invoked on the checkpoint and restore events. Such a CRaC-enablement of the application classes may come with more gains in startup performance.