IntelliJ IDEA Java

Java Runtimes: Insights From the Spring Boot Point of View

Read this post in other languages:

Choosing the right Java runtime for your Spring Boot project might seem trivial at first glance. After all, all runtimes are based on the OpenJDK code and offer the same APIs.

But not all runtimes are created equal. This article will discuss various metrics that could influence your decision to select a particular distribution of Java for your Spring Boot application.

This article was written by Aleksey Stukalov, Team Lead at JetBrains IntelliJ IDEA, and guest author Catherine Edelveis, Developer Advocate at BellSoft.

Developer’s perspective

Functional compatibility

For most developers, it doesn’t make much difference which Java distribution to use. It doesn’t even matter if you’re developing a Spring Boot application or using no framework at all. Why? The key here is the Technology Compatibility Kit (TCK).

The JDK itself is just a surface, with tons of different specifications hidden underneath. To ensure that a JDK implementation can correctly run your Java application, the implementation is verified by a massive set of tests called a TCK. Implementations that pass these tests hold the title of a “TCK-verified distribution”, which gives developers complete confidence that their application will behave in the same way with all TCK-verified Java runtimes.

While TCK-verified distributions guarantee the same functional behavior of your code, there may be other differences, but more on that below.

Tooling support

Based on the information above, there might seem to be no favored JDK distribution for developers, but in fact, this is not the case. Let’s see why some options tend to be better than others.

Gone are the days when downloading your JDK from the vendor’s website was the norm. Now, simply installing your preferred Java IDE ensures that you are equipped with the necessary runtime. If that is IntelliJ IDEA, the default JDK will be the JetBrains Runtime, a fork of OpenJDK that offers several enhancements on the original, like advanced class redefinition capabilities (DCEVM) for seamless code updates and an optional JCEF (a framework for embedding Chromium-based browsers). While these additions may not be on every development team’s requirements list, the fact that the JetBrains Runtime is embedded in the tooling and is available as a default makes it one of the most used JDKs for Java developers.

While the JetBrains Runtime is a default option in IntelliJ IDEA, you can download multiple JDKs in one click. Amazon Corretto, Azul Zulu, BellSoft Liberica JDK (Standard or with JavaFX), Eclipse Temurin, GraalVM CE or Oracle GraalVM, IBM Semeru, and SapMachine are available in the IDE, and are also popular among developers.

Spring Boot specifics

Spring Boot offers a convenient way to turn your application into a container image without writing a Dockerfile – buildpacks. However, not all Java runtimes can be used for that purpose. Paketo buildpacks used by Spring Boot offer several JDK distributions as a base for the container image. The default choice is Liberica JDK, which is also the Java runtime officially recommended by Spring. You can also use other JDKs, but remember that some offer only the JDK release, meaning the final container image will have a more significant memory footprint than the one based on JRE.

If you use Paketo buildpacks, it also makes sense to use the same JDK for development purposes. Even though functional compatibility is kept across all TCK-verified distributions, you still can have difficulties debugging memory leaks, performance issues, and other problems with differing JDKs.

DevOps perspective

As you get closer to going live, having a Spring Boot application becomes a bigger factor in your decision-making. Modern services end up in the clouds, where Kubernetes (K8s) has become the de-facto standard. So, we will take this setup as our target.

Tweak the K8s defaults

Deploying applications in the cloud is especially easy with Kubernetes, as it offers good settings by default for deployment and cluster orchestration. However, tweaking the defaults can save a company a fortune, especially for complex Spring Boot applications with substantial memory demands. A few recommendations for increasing the performance of your Java application in Kubernetes include:

  • Setting the CPU and RAM limits of K8s pods based on the results of load testing and stress testing aimed at assessing the application memory needs at a stable state and peak load.
  • Choosing a suitable garbage collector. Apart from the popular ParallelGC and G1GC, the latest Java versions include new GC implementations such as ZGC and ShenandoahGC (the latter not shipped with Oracle Java), designed for minimal latency with large heaps.
  • Using Kubernetes probes to monitor the health of your pods and be able to react promptly if anything goes wrong.

Use JRE in containers

The smaller the container image, the smaller the instance memory footprint and, thus, the lower your cloud bills. In addition, if your company uses its own Docker Registry, it tends to bloat over time, so saving memory is relevant in this case, too.

Containerized Spring Boot applications can get quite heavy. To keep them lean, you can choose the right base image with the JRE and a lightweight Linux distribution such as Alpine or Alpaquita.

The table below summarizes the base container images for JDK 21 available on Docker Hub. We compared the compressed JDK and JRE image sizes from various JDK vendors with a musl or glibc-based OS layer (distroless images were not part of the evaluation, as they may not be an optimal choice for some use cases). The data was collected on April 23, 2024. 

All images except for Liberica JDK and Red Hat OpenJDK are based on Alpine Linux (for musl) or Ubuntu (glibc). Liberica JDK images are based on Alpaquita Linux, which is 100% Alpine-compatible, but with two flavors: musl and glibc-based. Red Hat OpenJDK images are based on RHEL UBI 9.

Oracle Java builds are not available on Docker Hub.

 Liberica JDKAmazon CorrettoAzul ZuluIBM Semeru RuntimesEclipse TemurinRed Hat OpenJDK
JDK + glibc-based OS85.63 MB212.66 MB191.89 MB260.52 MB197.87 MB148.5 MB
JRE + glibc-based OS52.97 MB-*106.97 MB96.44 MB92.82 MB126.7 MB
JDK + musl-based OS78.02 MB155.72 MB157.66 MB167.05 MB
JRE + musl-based OS45.37 MB-*72.43 MB62.5 MB

*The JRE images for Amazon Corretto are not present on Docker Hub but are available on AWS. 

Upgrade to the latest Java

Newer Java versions are significantly more performant than the older ones thanks to numerous improvements to the JVM. This means that upgrading Java versions is essential to keep up with modern requirements for application performance in the cloud.

Besides, Spring Boot 3.x requires Java 17 as a baseline, so upgrading Java versions is paramount if you want to use the power of a new major version of your favorite framework.

However, if upgrading is not an option for you right now, some vendors such as Oracle and BellSoft offer a solution coupling JVM 17 and JDK 8 (BellSoft also has a similar solution for JDK 11). This makes your application believe it still runs on older Java versions, but in reality, it will have a newer engine. What does that mean for you? No Java or framework upgrade, little to no code changes, and an instant spike in latency and throughput!

Minimize startup time

Complex Spring Boot applications may take dozens of seconds to start up. Even the tiny reference app Spring Petclinic needs 5 to 7 seconds! Plus, Java applications consume more resources during warmup than they need when stable. This leads to:

  • Wasted budget on additional CPU cycles during warmup and on larger instances.
  • User dissatisfaction due to high service latency.
  • Inability to use the full potential of AWS Lambdas or similar services.

Luckily, Spring Boot 3 supports two of the most effective solutions for dealing with the issue of prolonged warmup – GraalVM Native Image and the Coordinated Restore at Checkpoint (CRaC) project. All you have to do is select a Java vendor who provides support for this functionality.

Coordinated Restore at Checkpoint (CRaC) 

CRaC enables you to pause a running Java application, save it to a file, and then restore it from the file from the moment it was paused, efficiently reducing startup from seconds to milliseconds.

Spring Boot integrates with CRaC starting with the 3.2 version. Two OpenJDK vendors currently offer JDK builds with CRaC support: Azul and BellSoft. Azul develops only the JDK and relies on Ubuntu for CRaC support. BellSoft provides CRaC-ed container images with their own Alpaquita Linux and Liberica JDK, so you can use CRaC in a Docker container without adjusting the JDKor Linux or adding additional configuration to the application except for the org.crac dependency.

The CRaC project is not TCK-verified yet. If you are looking for a production-ready feature, consider using the Native Image technology.

Native Image

The Native Image technology enables developers to convert Java applications into static native images ahead-of-time under the closed-world assumption. Native images start up almost instantly and may have a smaller footprint, as they don’t include the JDK.

Other than Oracle, which ships Native Image as part of Oracle GraalVM, two JDK vendors offer the native-image compiler as part of their Java offering:

  • Mandrel by Red Hat is tailored to the Quarkus framework.
  • Liberica Native Image Kit by BellSoft is used as the default native-image compiler in Spring buildpacks and is recommended by the Spring team. It is based on GraalVM CE but includes ParallelGC, which is absent in the GraalVM Community Edition.

SecOps perspective

As cyberattacks grow in number and aggression, maintaining project security at a high level gets more challenging every year. For your Java applications to stay secure, you have to keep a watchful eye on the dependencies and updates.

Use a Software Bill of Materials

A Software Bill of Materials (SBOM) is integral to software supply chain security. It helps you monitor the state of all runtime dependencies by providing data on component versions, licenses, and known CVEs.

You can now conveniently create an SBOM for your project using the Maven and Gradle plugins so that your customers know about the state of your dependencies. However, you should also be confident in the solutions you use for development, including the Java runtime. Choosing a Java runtime whose vendor provides an SBOM is a good option for keeping your infrastructure secure.

Having an SBOM for container images you use for deployment is also important, because if you don’t know about the internals of a base image, you can’t react promptly if they are infected with known CVEs. This, in turn, makes your production highly vulnerable to attacks. You can generate an SBOM using existing tools such as Docker Scout, but receiving a ready SBOM from the vendor is more convenient.

Update Java regularly

Java runtimes that receive quarterly builds with security patches and critical patches help keep your application’s foundation free from known CVEs.

Major JDK vendors provide quarterly PSU releases, and three of them – Oracle, BellSoft, and Azul – additionally release stabilized CPU builds for LTS versions and the latest feature release with patches and only critical fixes. CPU builds enable you to introduce security patches without disrupting the production environment.

Quarterly releases are issued in accordance with a support roadmap for LTS versions, but the support lifecycle of LTS releases, summarized in the table below, varies from vendor to vendor.

Oracle JDKLiberica JDKAmazon CorrettoAzul ZuluIBM Semeru RuntimesEclipse Temurin*Red Hat OpenJDK
End of commercial support for older Java versions 
JDK 6March 2026December 2027
JDK 7March 2026December 2027
JDK 8December 2030March 2031May 2026December 2030November 2026November 2026
JDK 11September 2026March 2032September 2027January 2032November 2026October 2024
Support period for newer Java versions
JDK 17+8 years8.5 years7 years8 years6 years6 years

Support for Eclipse Temurin is provided by third-party vendors.

Conclusion

Which Java runtime you use for Spring Boot development may not be crucial as long as it’s TCK-verified. However, there are important metrics such as performance, the range of container images, additional solutions for efficient cloud deployment, and others that make some runtimes a more suitable choice for your Spring Boot project.

Summing up everything we’ve discussed in this article, the table below outlines the key features aiding Spring Boot development and the Java runtimes offering them.

FeatureOracle JDKLiberica JDKAmazon CorrettoAzul ZuluIBM Semeru RuntimesEclipse TemurinRed Hat OpenJDK
TCK-verification+++++++
Recommended by Spring+
Multiple Installers and Packages(tar, deb, MSI, DMG, JDK/JREs)No apkNo pkgNo JRE+No dmg+++No dmgNo debNo apkNo rpmNo pkg
Package managers, Docker images, installation directly from IntelliJ IDEA+++++Not available from IDE
Package with JavaFX+For In version 8 for specific platforms only ++
Paketo buildpacks for Spring BootJDK onlyVersion 17++(default)JDK only++
Alpine Linux support+++++
Smallest base image with JRE45.37 MB72.43 MB96.44 MB62.5 MB126.7 MB
GraalVM Native Image CompilerAs part of Oracle GraalVMLiberica Native Image Kit (default native-image compiler for Spring Boot)Mandrel for Quarkus
CRaC support++
Free updates lifecycle for LTS versions3 years8.5 years7 years8 years6 years6 years6 years
Stabilized CPU builds+++
SBOM+++++++
Solution for increasing performance of Spring Boot 2.x projects+(For JDK 8)+(For JDK 8 and 11)
Commercial support available++For AWS only++Not from Adoptium+

Which JRE do you use to develop and deploy your Spring Boot applications? Let us know in the comments below.

image description