Exploring Java 21: Upgrade Insights, Generational ZGC, and Virtual Threads
This article reviews Oracle's Java 21 LTS release, outlines its new JEP features, compares it with Java 17, shares upgrade experiences including pom updates and runtime fixes, and evaluates generational ZGC and virtual threads with performance tests and code examples.
Introduction
Oracle released Java 21 as the latest LTS version, prompting the author to upgrade a personal project and share the findings.
Java 21 Update Overview
Official release notes and community introductions are linked for further reading.
New Features
JEP 431: Sequenced Collections
JEP 439: Generational ZGC
JEP 440: Record Patterns
JEP 441: Switch Pattern Matching
JEP 444: Virtual Threads
JEP 449: Deprecate Windows x86
JEP 451: Prepare to Disallow Dynamic Proxy Loading
JEP 452: Key Encapsulation Mechanism API
JEP 430: String Templates (Preview)
JEP 442: Foreign Function and Memory API (Third Preview)
JEP 443: Unnamed Patterns and Variables (Preview)
JEP 445: Unnamed Classes and Instance Main Methods (Preview)
JEP 446: Scoped Values (Preview)
JEP 453: Structured Concurrency (Preview)
JEP 448: Vector API (Incubator 6)
Comparison with Java 17
The JDK binary size grew from 289 MB to 320 MB, the module count decreased by one, and the packaging metaphor changed from stainless steel to titanium. The following images illustrate the differences:
Upgrade Experience
Download links:
OpenJDK: https://jdk.java.net/21/
Oracle: https://www.oracle.com/java/technologies/downloads/
After updating the
pom.xml, the project compiled but failed at runtime with the error:
<code>java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'</code>Upgrading Lombok to version 1.18.30 resolved the issue.
Compatibility check revealed a charset problem with
PopupMenuused in the system tray.
Generational ZGC Exploration
Generational ZGC reduces allocation stalls, heap memory overhead, and GC CPU overhead. It can be enabled with the JVM options
-XX:+UseZGC -XX:+ZGenerational. Preliminary memory usage measured with the MooInfo tool is shown below:
Virtual Thread Exploration
Virtual threads are lightweight threads that simplify writing, maintaining, and debugging high‑throughput concurrent applications. They differ from platform threads, which are thin wrappers over OS threads.
Key points:
Implementation is analogous to virtual memory mapping.
Many virtual threads are multiplexed onto a few OS threads.
Virtual threads typically have shallow call stacks and are suited for I/O‑bound tasks.
Although they support thread‑local variables, a single JVM can host millions of virtual threads.
Sample code for creating and using virtual threads:
<code>Thread thread = Thread.ofVirtual().start(() -> System.out.println("Hello"));
thread.join();</code> <code>try {
Thread.Builder builder = Thread.ofVirtual().name("MyThread");
Runnable task = () -> System.out.println("Running thread");
Thread t = builder.start(task);
System.out.println("Thread t name: " + t.getName());
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}</code> <code>public class CreateNamedThreadsWithBuilders {
public static void main(String[] args) {
try {
Thread.Builder builder = Thread.ofVirtual().name("worker-", 0);
Runnable task = () -> System.out.println("Thread ID: " + Thread.currentThread().threadId());
Thread t1 = builder.start(task);
t1.join();
System.out.println(t1.getName() + " terminated");
Thread t2 = builder.start(task);
t2.join();
System.out.println(t2.getName() + " terminated");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}</code>Using the virtual‑thread‑per‑task executor:
<code>try (ExecutorService myExecutor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<?> future = myExecutor.submit(() -> System.out.println("Running thread"));
future.get();
System.out.println("Task completed");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}</code>Performance Comparison
A simple benchmark compared 100 virtual threads versus 100 platform threads for I/O‑bound file reads and HTTP GET requests to Baidu. Virtual threads showed clear advantages for I/O‑bound workloads, while CPU‑bound tasks exhibited similar performance.
Record Patterns
Java 21 introduces record patterns, allowing concise deconstruction of record components.
<code>static void printSum(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println(x + y);
}
}
</code>Reference material and further reading links are provided at the end of the article.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.