What’s new in Java 21: A Tour of its Most Exciting Features
- 4.6/5
- 2641
- Jul 20, 2024
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.