Skip to main content

What's new in Java 21: A Tour of its Most Exciting Features

The Java platform now follows a six-month release cycle, and Java 21 has succeeded Java 17 as the most recent long-term support release (LTS).

JDK 21 features a total of 15 JEPs (JDK Enhancement Proposals). You can view the complete list on the official Java site here. In this document, I'll highlight several Java 21 JEPs that I find particularly noteworthy.

1) Sequenced collections

Java 21 introduces a new Java Collection Hierarchy, enhancing operations on ordered datasets. This new API not only facilitates convenient addition and deletion of the first and last elements of a collection but also enables reversing sequences.

2) Pattern Matching for switch

Pattern Matching for switch builds upon the extended instanceof expression.

Before Java 21

In Java 21

Pattern Matching

Pattern Matching using When

3) Record Patterns

Record patterns are a new feature, first introduced in Java 19 as a preview.

Before Java 21

In Java 21

Record Patterns

Nested Record Patterns


3) Virtual Threads

The Virtual Threads feature is the most exciting addition in Java 21.

Why Virtual Threads ?

We can estimate that a single java.lang.Thread consumes approximately 2 to 8 MB of memory, depending on the OS and configuration. Additionally, each Java Thread is mapped 1:1 to a kernel thread. In simple web applications that use a "one thread per request" approach, this can quickly overwhelm/kill our server when traffic increases.

Thread-4059 is running
        Thread-4060 is running
        [0.751s][warning][os,thread] Failed to start thread "Unknown thread" - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 4k, detached.
        [0.751s][warning][os,thread] Failed to start the native thread for java.lang.Thread "Thread-4061"
        Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
        at java.base/java.lang.Thread.start0(Native Method)
        at java.base/java.lang.Thread.start(Thread.java:802)
        at tb.NoVirtualThreads.main(NoVirtualThreads.java:19)

        Process finished with exit code 1

Running Virtual Threads ?

Virtual Threads are not managed or scheduled by the operating system; instead, their scheduling is handled by the JVM. While actual tasks need to be executed, the JVM uses carrier threads, which are essentially platform threads, to execute any virtual thread when it's ready.

Virtual Threads are designed to be lightweight and consume significantly less memory than standard platform threads.

.
.
.
[VirtualThread[#1000039,virtual-thread-999981]/runnable@ForkJoinPool-1-worker-13]
[VirtualThread[#1000041,virtual-thread-999983]/runnable@ForkJoinPool-1-worker-9]
[VirtualThread[#1000043,virtual-thread-999985]/runnable@ForkJoinPool-1-worker-13]
[VirtualThread[#1000046,virtual-thread-999988]/runnable@ForkJoinPool-1-worker-9]
[VirtualThread[#1000048,virtual-thread-999990]/runnable@ForkJoinPool-1-worker-13]
[VirtualThread[#1000051,virtual-thread-999993]/runnable@ForkJoinPool-1-worker-9]
[VirtualThread[#1000053,virtual-thread-999995]/runnable@ForkJoinPool-1-worker-13]
.
.
.

ForkJoinPool-1-worker-X platform threads serve as carrier threads that manage our virtual threads. We observe that virtual threads numbered 999981, 999985, 999990, and so on, are all using the same carrier thread, number 13.

When a virtual thread encounters a blocking operation, such as an I/O task, the JVM efficiently detaches it from the underlying physical thread (the carrier thread). This detachment is crucial because it frees up the carrier thread to run other virtual threads instead of remaining idle, waiting for the blocking operation to finish.

Consequently, a single carrier thread can multiplex many virtual threads, potentially numbering in the thousands or even millions, depending on available memory and the nature of the tasks.

Note: Virtual threads are not the only way to handle this problem. Asynchronous programming (using frameworks like WebFlux or native Java APIs like CompletableFuture) is another approach.


4) String Templates (Preview)

The Spring Templates feature remains in preview mode. To utilize it, you need to include the "--enable-preview" flag in your compiler arguments.

Before Java 21

In Java 21

5) Generational ZGC

Enhance application performance by enhancing the Z Garbage Collector (ZGC) to manage separate generations for young and old objects. This enhancement enables ZGC to collect young objects—commonly short-lived—more frequently.

ZGC was initially introduced into JDK 11 as an experimental garbage collector (GC) implementation. Subsequently, with the implementation of JEP-377, it transitioned into a production-ready feature in JDK 15.

In essence, ZGC aims to minimize stop-the-world phases to the greatest extent possible. It achieves this by ensuring that the duration of these pause times remains independent of the heap size. These attributes make ZGC well-suited for server applications, particularly those with large heaps where rapid application response times are crucial.

You can find further information on memory management in Java here, Garbage collector basics here, and learn more about ZGC here.

6) Other Features

In addition to the previously mentioned features, Java 21 also introduces several additional preview features:

1) Foreign Function & Memory API (Third Preview) (https://openjdk.org/jeps/442)
2) Unnamed Patterns and Variables (Preview) (https://openjdk.org/jeps/443)
3) Unnamed Classes and Instance Main Methods (Preview) (https://openjdk.org/jeps/445)
4) Scoped Values (Preview) (https://openjdk.org/jeps/446)
5) Vector API (Sixth Incubator) (https://openjdk.org/jeps/448)

And few others as well.

Comments

Popular posts from this blog

Deploying Spring Boot microservices on Kubernetes Cluster

This article guides you through the deployment of two Spring Boot microservices, namely "order-service" and "inventory-service," on Kubernetes using "MiniKube" . We will establish communication between them, with "order-service" making calls to an endpoint in "inventory-service." Additionally, we will configure "order-service" to be accessible from the local machine's browser . 1) Create Spring Boot microservices The Spring Boot microservices, "order-service" and "inventory-service," have been developed and can be found in this GitHub repository. If you are interested in learning more about creating Spring Boot REST microservices, please refer to this or this (Reactive) link. 2) Build Docker Images The Docker images for both "order-service" and "inventory-service" have already been generated and deployed on DockerHub, as shown below. codeburps/order-service cod...

Circuit Breaker Pattern with Resilience4J in a Spring Boot Application

Read Also: Spring Cloud Circuit Breaker + Resilience4j Resilience4j is a lightweight fault tolerance library that draws inspiration from Netflix Hystrix but is specifically crafted for functional programming. The library offers higher-order functions, known as decorators , designed to augment any functional interface, lambda expression, or method reference with features such as Circuit Breaker, Rate Limiter, Retry, or Bulkhead . These functionalities can be seamlessly integrated within a project, class, or even applied to a single method. It's possible to layer multiple decorators on any functional interface, lambda expression, or method reference, allowing for versatile and customizable fault tolerance. While numerous annotation-based implementations exist online, this article focuses solely on the reactive approach using router predicates and router functions . How Circuit Breaker Pattern works? In general, a circuit breaker functions as an automatic electrical s...

How to create a basic Spring 6 project using Maven

Below is a step-by-step guide to creating a basic Spring project using Maven. 1) Create a Maven Project Use the following Maven command to create a new Maven project. mvn archetype:generate -DgroupId=com.tb -DartifactId=spring-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 2) Import in IntelliJ IDEA If you haven't already, open IntelliJ IDEA on your system. Go to "File" > "New" > "Project from Existing Sources..." . In the file dialog, navigate to the directory where your Maven project is located. Select the pom.xml file within the project directory and click "Open." 3) Update pom.xml In total, the application requires the below-mentioned dependencies: 4) Create Spring Configuration Create a Java configuration class that uses annotations to define your Spring beans and their dependencies. This class should be annotated with @Configuration . 5) Create the Main Application C...