<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Java21 on Devops Monk</title><link>https://devops-monk.com/tags/java21/</link><description>Recent content in Java21 on Devops Monk</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 04 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://devops-monk.com/tags/java21/index.xml" rel="self" type="application/rss+xml"/><item><title>Generational ZGC (JEP 439): Sub-Millisecond Pauses at Any Scale</title><link>https://devops-monk.com/tutorials/java21/generational-zgc/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/generational-zgc/</guid><description>Why GC Still Matters in 2024 Even with better APIs and faster hardware, garbage collection pauses remain one of the top causes of latency spikes in Java services. A 200ms GC pause in a payment processing service is a customer-visible failure. A 50ms pause in a trading system is a missed execution window.
Java 21 delivers Generational ZGC — the most capable GC Java has ever shipped — as a production-ready, non-preview feature.</description></item><item><title>Java 21 Production Checklist and Performance Best Practices</title><link>https://devops-monk.com/tutorials/java21/production-best-practices/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/production-best-practices/</guid><description>The Production Mindset Migrating to Java 21 unlocks new capabilities, but production readiness requires deliberate configuration. The JVM defaults are conservative — designed to work reasonably across a wide range of workloads, not to be optimal for any specific one.
This article covers:
Which JVM flags to set for every production Java 21 deployment GC selection and tuning for different workload profiles Virtual thread configuration and monitoring Container-aware JVM settings Observability and profiling Startup and memory optimization JVM Flags: The Production Baseline Start every Java 21 production deployment with this baseline flag set:</description></item><item><title>Java 21: The LTS Release That Changes Everything</title><link>https://devops-monk.com/tutorials/java21/java21-overview/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/java21-overview/</guid><description>Why Java 21 Is Different Every third Java release is an LTS — Long-Term Support. Java 21 is the fourth LTS after Java 8, 11, and 17. But unlike previous LTS releases, which were largely incremental, Java 21 delivers features that fundamentally change how you write concurrent code, how the JVM manages memory and GC, and how Java competes with dynamic languages for expressiveness.
Three years of Project Loom work lands as Virtual Threads — production-ready, requiring zero framework changes for most Spring Boot or Jakarta EE applications.</description></item><item><title>Key Encapsulation Mechanism API (JEP 452): Post-Quantum Cryptography in Java</title><link>https://devops-monk.com/tutorials/java21/kem-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/kem-api/</guid><description>Why Post-Quantum Cryptography Now? Classical public-key cryptography (RSA, ECDH) relies on mathematical problems that are hard for classical computers — factoring large integers or solving the discrete logarithm problem. A sufficiently powerful quantum computer running Shor&amp;rsquo;s algorithm could solve these problems efficiently, breaking all existing RSA and ECC-based security.
Quantum computers capable of breaking 2048-bit RSA don&amp;rsquo;t exist yet. But &amp;ldquo;harvest now, decrypt later&amp;rdquo; attacks are real: adversaries intercept and store encrypted traffic today, planning to decrypt it once quantum computers mature.</description></item><item><title>Migrating to Java 21: From Java 8, 11, and 17 — Step by Step</title><link>https://devops-monk.com/tutorials/java21/migration-guide/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/migration-guide/</guid><description>Why Migrate Now? Java 21 is the current Long-Term Support (LTS) release, and it is the most feature-rich LTS since Java 8. LTS releases receive security patches and bug fixes for years. Java 11, the previous widely-used LTS, reached its extended support window end depending on your vendor. Java 8 mainstream support ended in 2019.
More concretely, Java 21 brings:
Virtual Threads — drop-in replacement for platform threads, enabling massive concurrency without reactive rewrites Pattern Matching for switch and records — eliminating entire categories of verbose, error-prone instanceof/cast chains Sequenced Collections — a unified API for ordered collection types Generational ZGC — sub-millisecond GC pauses at any heap size These are not incremental improvements.</description></item><item><title>Pattern Matching for switch (JEP 441): Type Dispatch Without the Boilerplate</title><link>https://devops-monk.com/tutorials/java21/pattern-matching-switch/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/pattern-matching-switch/</guid><description>The Problem: Cascading instanceof Chains Any Java codebase handling multiple subtypes has code like this:
// Java 16 and earlier — the bad old way static double calculateArea(Shape shape) { if (shape instanceof Circle) { Circle c = (Circle) shape; return Math.PI * c.radius() * c.radius(); } else if (shape instanceof Rectangle) { Rectangle r = (Rectangle) shape; return r.width() * r.height(); } else if (shape instanceof Triangle) { Triangle t = (Triangle) shape; return 0.</description></item><item><title>Record Patterns (JEP 440): Destructuring Records with Power and Precision</title><link>https://devops-monk.com/tutorials/java21/record-patterns/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/record-patterns/</guid><description>Records Recap Records (Java 16, JEP 395) are transparent carriers of immutable data:
record Point(int x, int y) {} record ColoredPoint(Point point, String color) {} record Line(Point start, Point end) {} The compiler generates a constructor, accessor methods (x(), y()), equals, hashCode, and toString. Before Java 21, accessing record components required calling accessor methods:
Object obj = new ColoredPoint(new Point(3, 4), &amp;#34;red&amp;#34;); if (obj instanceof ColoredPoint cp) { int x = cp.</description></item><item><title>Scoped Values (JEP 446): Thread-Safe Context Without ThreadLocal</title><link>https://devops-monk.com/tutorials/java21/scoped-values/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/scoped-values/</guid><description>Preview Feature — Requires --enable-preview at compile and runtime.
The ThreadLocal Problem at Scale ThreadLocal has been the standard way to pass context (request ID, user session, database transaction) implicitly through a call stack without threading it through every method signature. It works well with a few hundred platform threads. With virtual threads, it breaks down.
Problem 1 — Memory overhead with inheritance
When you create a child thread with InheritableThreadLocal, Java copies the thread-local map from parent to child:</description></item><item><title>Sequenced Collections (JEP 431): A Unified API for Ordered Collections</title><link>https://devops-monk.com/tutorials/java21/sequenced-collections/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/sequenced-collections/</guid><description>The 30-Year Gap in the Collections API Java&amp;rsquo;s Collections Framework has a fundamental inconsistency: there is no uniform way to access the first or last element of an ordered collection. Before Java 21:
// List — index-based var list = List.of(&amp;#34;a&amp;#34;, &amp;#34;b&amp;#34;, &amp;#34;c&amp;#34;); String first = list.get(0); // first element String last = list.get(list.size() - 1); // last element — verbose, error-prone // Deque — special methods Deque&amp;lt;String&amp;gt; deque = new ArrayDeque&amp;lt;&amp;gt;(List.</description></item><item><title>Setting Up Java 21: JDK Options, Tooling, and IDE Configuration</title><link>https://devops-monk.com/tutorials/java21/java21-setup/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/java21-setup/</guid><description>Choosing a JDK Distribution Java 21 is open source (OpenJDK). Multiple vendors distribute binaries, all built from the same source with identical features. Choose based on your support requirements:
Distribution Vendor Free LTS Support Notes Eclipse Temurin Adoptium Yes 2028+ Recommended for most developers OpenJDK Oracle/Community Yes 6 months only Reference implementation, no LTS Oracle JDK Oracle Yes (NFTC) 2031 Identical to OpenJDK since Java 17 Amazon Corretto AWS Yes 2030+ Good for AWS deployments Microsoft Build of OpenJDK Microsoft Yes Long-term Good for Azure deployments Azul Zulu Azul Yes/Paid Long-term Includes Zing JVM variant GraalVM Oracle Yes Long-term Adds native-image, polyglot Recommendation: Eclipse Temurin for local development; match your cloud provider&amp;rsquo;s JDK for production (Corretto on AWS, Microsoft OpenJDK on Azure).</description></item><item><title>Structured Concurrency (JEP 453): Safe, Readable Concurrent Code</title><link>https://devops-monk.com/tutorials/java21/structured-concurrency/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/structured-concurrency/</guid><description>Preview Feature — Requires --enable-preview at compile and runtime. The API stabilized significantly from Java 21 through 24 and will be finalized in a future release.
The Unstructured Concurrency Problem When you submit tasks to an ExecutorService, the tasks are logically related but structurally unconnected — the executor doesn&amp;rsquo;t know they belong together:
// Unstructured concurrency — looks simple, hides serious problems ExecutorService executor = Executors.newCachedThreadPool(); Future&amp;lt;Order&amp;gt; orderFuture = executor.</description></item><item><title>Unnamed Classes and Instance Main Methods (JEP 445): Java for Scripts and Beginners</title><link>https://devops-monk.com/tutorials/java21/unnamed-classes/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/unnamed-classes/</guid><description>Preview Feature — Requires --enable-preview at compile and runtime.
The Boilerplate Problem Teaching Java to a beginner means explaining class declarations, access modifiers, static, and String[] before they can print &amp;ldquo;Hello, World!&amp;rdquo;:
// Traditional Java — 4 concepts before printing one line public class HelloWorld { public static void main(String[] args) { System.out.println(&amp;#34;Hello, World!&amp;#34;); } } Every word carries meaning, but that meaning is irrelevant until the student understands OOP, the JVM class model, and program entry points.</description></item><item><title>Unnamed Patterns and Variables (JEP 443): Writing Intent-Clear Code</title><link>https://devops-monk.com/tutorials/java21/unnamed-patterns/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/unnamed-patterns/</guid><description>Preview Feature — Requires --enable-preview at compile and runtime. The _ identifier was reserved in Java 9; this JEP gives it formal semantics.
The Problem: Forced Naming of Unused Things When pattern matching, you often care about only some components of a matched structure. But the syntax forces you to name everything:
record Point(int x, int y) {} record ColoredPoint(Point point, String color) {} record Box(ColoredPoint cp, int weight) {} // You only care about the color — but must name everything else if (obj instanceof Box(ColoredPoint(Point(int x, int y), String color), int weight)) { System.</description></item><item><title>Vector API (JEP 448): SIMD Computation in Java</title><link>https://devops-monk.com/tutorials/java21/vector-api/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/vector-api/</guid><description>Preview Feature in Java 21 — The Vector API has been in preview since Java 16 (JEP 338). JEP 448 is the sixth preview iteration in Java 21. The API is stable and production-usable with --enable-preview; finalization is pending Project Valhalla value types.
What Is SIMD and Why Does It Matter? Modern CPUs can perform the same arithmetic operation on multiple data values in a single instruction. This is called SIMD — Single Instruction, Multiple Data.</description></item><item><title>Virtual Threads (JEP 444): A Million Threads Without the Pain</title><link>https://devops-monk.com/tutorials/java21/virtual-threads/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://devops-monk.com/tutorials/java21/virtual-threads/</guid><description>The Platform Thread Problem Every Java thread since Java 1.0 maps 1:1 to an OS thread. OS threads are heavy:
Stack memory: 512 KB – 2 MB per thread (configurable with -Xss, default 512 KB on Linux) Context switch cost: ~1–10 μs per switch (kernel mode transition + cache invalidation) Hard limit: A 64-bit machine with 8 GB RAM can support roughly 8,000–16,000 OS threads before running out of stack memory This limit shapes how Java servers are built.</description></item></channel></rss>