Vier JEPs bringen tatsächlich neue beziehungsweise erstmals standardmäßig aktivierte Funktionalität: Compact Object Headers werden zum Default, G1 wird unabhängig von der Umgebung zum Standard-Garbage-Collector, TLS 1.3 erhält einen hybriden Post-Quantum-Schlüsselaustausch und der Java Flight Recorder schwärzt sensible Informationen bereits während der Aufzeichnung. Die fünf weiteren JEPs sind Wiedervorlagen bereits bekannter Preview- oder Inkubator-Features. Die Liste der umgesetzten JEPs ist daher diesmal nicht allzu lang [1]:
-
523: Make G1 the Default Garbage Collector in All Environments
-
527: Post-Quantum Hybrid Key Exchange for TLS 1.3
-
531: Lazy Constants (Third Preview)
-
532: Primitive Types in Patterns, instanceof, and switch (Fifth Preview)
-
533: Structured Concurrency (Seventh Preview)
-
534: Compact Object Headers by Default
-
536: JFR In-Process Data Redaction
-
537: Vector API (Twelfth Incubator)
-
538: PEM Encodings of Cryptographic Objects (Third Preview)
Trotzdem sind da einige spannende Themen und Entwicklungen dabei. Und wie üblich finden sich auch zahlreiche kleinere Änderungen in APIs und Laufzeitumgebung, die durchaus einen Blick wert sind. Und nur weil das Release klein erscheint, bedeutet „ruhiger“ nicht „unbedeutend“.
Vielmehr passieren unter der Haube einige Vorbereitungen für die nächste große Baustelle: Project Valhalla. Denn direkt nach dem Feature-Freeze zum OpenJDK 27 im Juni 2026 wurden die ersten großen Änderungen zu Value Classes als Vorbote für das OpenJDK 28 in das Haupt-Git-Repo gemerged [2]. Das wird die größte Veränderung des Java-Typsystems seit Generics.
Die Releases im Jahr 2026 sind also Teil einer Übergangsphase – die Plattform wird konzeptionell, technisch und sicherheitsseitig auf die nächsten großen Änderungen vorbereitet. Das OpenJDK 27 ist kein spektakuläres Featurerelease, aber ein strategisches. Es konsolidiert Performance, Nebenläufigkeit, Sicherheitsmodell und bereitet so auf Value Classes, stärkere Encapsulation, strukturelle Pattern-Konzepte und KI-Workloads vor. Mit anderen Worten, es ist Teil einer Evolution, die das Fundament der Plattform modernisiert – ohne ihre Stabilität und vor allem die Abwärtskompatibilität zu gefährden.
Compact Object Headers werden Standard
Jedes Java-Objekt besitzt neben seinen eigentlichen Daten einen Header. Die HotSpot VM speichert darin unter anderem Informationen über die Klasse des Objekts, dessen Identity Hash Code, Synchronisation und Garbage Collection. Bei einer typischen 64-Bit-JVM mit komprimierten Class Pointern bestand dieser Header bisher aus einem 64 Bit großen Mark Word und einem 32 Bit großen Class Word, also insgesamt aus 96 Bit bzw. 12 Byte. Gerade bei vielen kleinen Objekten haben diese 12 Byte einen erheblichen Anteil am gesamten Speicherverbrauch.
Project Lilliput arbeitet deshalb schon länger daran, diesen Overhead zu reduzieren. Die mit Java 24 zunächst experimentell eingeführten Compact Object Headers integrieren den Class Pointer in das Mark Word und reduzieren den Header damit auf 64 Bit beziehungsweise acht Byte. Java 25 machte die Technik zu einem regulären Feature, sie musste aber weiterhin explizit aktiviert werden. Der JEP 534 macht Compact Object Headers mit Java 27 nun zum Standard. Wer zum bisherigen Layout zurückkehren will, kann es vorerst noch deaktivieren:
java ... -XX:-UseCompactObjectHeaders
Vier eingesparte Byte erscheinen zunächst wenig. Java-Anwendungen bestehen aber häufig aus sehr vielen kleinen Objekten. Je kleiner ein Objekt ist, desto größer ist der relative Anteil seines Headers. Vom OpenJDK veröffentlichte Messungen zeigen entsprechend deutliche Effekte. In einem SPECjbb2015-Szenario sank der Heap-Bedarf um 22 Prozent und die CPU-Zeit um 8 Prozent. In anderen Messungen sank die Zahl der Garbage Collections um 15 Prozent. Ein paralleler JSON-Parser lief rund 10 Prozent schneller.
Das liegt nicht nur am geringeren Heap-Verbrauch. Kleinere Objekte verbessern auch die Datenlokalität und damit die Nutzung der CPU-Caches. Gleichzeitig muss der Garbage Collector weniger Speicher verarbeiten. Wie stark eine reale Anwendung profitiert, hängt natürlich vom Objektmodell ab. Bei Millionen kleiner Objekte kann der Effekt erheblich sein, bei wenigen sehr großen Arrays dagegen kaum. Für normalen Java-Code ist die Änderung aber transparent. Etwas mehr Aufmerksamkeit verdient die Änderung in nächster Zeit bei JVM-nahen Tools, Agents oder Bibliotheken, die Annahmen über das konkrete Speicherlayout von Objekten treffen. Sie müssen gegebenenfalls angepasst beziehungsweise aktualisiert werden.
G1 wird wirklich überall zum Default
Seit Java 9 gilt G1 als Standard-Garbage-Collector der HotSpot VM. Ganz korrekt war diese Aussage bisher allerdings nicht. Auf besonders kleinen Systemen wählte die JVM stattdessen automatisch den Serial GC. Das betraf Umgebungen mit nur einer verfügbaren CPU oder weniger als 1 792 MByte physischem Speicher. Zum Zeitpunkt der Einführung von G1 als Default bot Serial dort Vorteile bei Durchsatz und Speicherbedarf. Inzwischen wurde G1 jedoch auch für kleine Heaps und eingeschränkte Umgebungen erheblich optimiert. Insbesondere die mit Java 26 vorgenommenen Verbesserungen zur Reduzierung von Synchronisation haben den Abstand beim Durchsatz weiter verringert. Der JEP 523 entfernt deshalb die Sonderregel nun. Wenn kein Garbage Collector explizit angegeben wird, verwendet Java 27 immer G1. Aber der Serial GC verschwindet dadurch nicht. Er lässt sich weiterhin explizit auswählen:
java ... -XX:+UseSerialGC
Für die meisten Anwendungen ändert sich ebenfalls nichts. Wer bereits auf einer Maschine mit mehreren CPUs und ausreichend RAM arbeitet, nutzte ohnehin G1. Interessant ist die Änderung dagegen für kleine Container (Docker, …). Bislang konnte die Änderung des CPU- oder der Memory-Limits dazu führen, dass dieselbe Anwendung plötzlich mit einem anderen Garbage Collector startete. Dieses implizite Verhalten entfällt nun.
Stay tuned
Regelmäßig News zur Konferenz und der JAX-Community erhalten
Post-Quantum-Kryptografie für TLS 1.3
Eine der wichtigsten Neuerungen von Java 27 beschäftigt sich mit einer Bedrohung, die heute noch gar nicht praktisch existiert. Heutige TLS-Verbindungen verwenden für den Schlüsselaustausch unter anderem elliptische Kurven. Ein ausreichend leistungsfähiger Quantencomputer könnte die dafür zugrunde liegenden mathematischen Probleme mit dem Shors-Algorithmus sehr schnell knacken. Solche Computer stehen heute zwar noch nicht zur Verfügung; für langlebige vertrauliche Daten ergibt sich trotzdem bereits ein Problem. Ein Angreifer kann verschlüsselten Netzwerkverkehr heute aufzeichnen und ihn möglicherweise Jahre später entschlüsseln. Dieses Szenario wird als „harvest now, decrypt later“ bezeichnet.
Mit JEP 527 unterstützt die TLS-1.3-Implementierung von Java deshalb drei neue, hybride Schlüsselaustauschverfahren:
-
X25519MLKEM768
-
SecP256r1MLKEM768
-
SecP384r1MLKEM1024
Sie kombinieren jeweils einen klassischen ECDHE-Schlüsselaustausch mit dem quantenresistenten ML-KEM. Dieser hybride Ansatz ist wichtig, um verschiedene Verfahren zu kombinieren. Gerade die Post-Quantum-Algorithmen sind noch jünger als etablierte Verfahren wie X25519 und haben noch nicht dieselbe jahrzehntelange Analyse hinter sich. Die Verbindung ist sicher, solange mindestens eines der beiden Verfahren nicht gebrochen werden kann. Damit schützt der klassische Anteil vor möglichen zukünftigen Schwächen von ML-KEM, während ML-KEM den Schutz gegen Quantenangriffe auf ECDHE übernimmt.
Diese wichtige Änderung ist für unsere Anwendungen weitgehend transparent. X25519MLKEM768 steht in Java 27 an erster Stelle der standardmäßig angebotenen TLS-1.3-Gruppen. Unterstützt die Gegenstelle ebenfalls dieses Verfahren, wird es automatisch ausgehandelt. Bestehender JSSE-, HTTPS- oder HttpClient-Code profitiert also allein durch das JDK-Update. Und die Auswahl lässt sich bei Bedarf über jdk.tls.namedGroups oder programmatisch über SSLParameters.setNamedGroups() beeinflussen. Das OpenJDK hat alle dafür notwendigen Bausteine schon zuvor eingeführt: Java 21 brachte das Key Encapsulation Mechanism API, Java 24 ML-KEM. Java 27 integriert diese Bausteine nun erstmals in das alltäglich verwendete TLS.
JFR schwärzt sensible Daten
Java Flight Recorder (JFR) ist ein äußerst nützliches Werkzeug zur Diagnose von Produktionssystemen. Gerade deshalb werden .jfr-Dateien häufig zwischen Teams ausgetauscht oder an Hersteller und Support-Dienstleister weitergegeben. Ein Flight Recording kann allerdings auch Informationen enthalten, die man nicht unbedingt weitergeben möchte. Dazu gehören unter anderem:
-
Kommandozeilenargumente
-
Umgebungsvariablen oder
-
System-Properties
Und genau dort finden sich in realen Anwendungen immer wieder Passwörter, Tokens oder andere Zugangsdaten. Zwar lassen sich JFR-Dateien nachträglich bereinigen. Zu diesem Zeitpunkt könnten diese Geheimnisse aber schon abgegriffen worden sein. JEP 536 verlagert die Schwärzung deshalb direkt in die JVM. Sensible Werte werden bereits beim Erzeugen des Events durch [REDACTED] ersetzt und gelangen so gar nicht erst in die Datei. Die Funktion ist mit Java 27 standardmäßig aktiviert.
Beispielsweise wird ein Recording statt javax.net.ssl.keyStorePassword = secret123 nur noch javax.net.ssl.keyStorePassword = [REDACTED] enthalten. Die Erkennung basiert auf konfigurierbaren Filtern für Namen von System Properties und Umgebungsvariablen sowie für Kommandozeilenargumente. Die Defaultregeln erkennen typische Begriffe wie password, secret, token, credential, api-key oder client-secret. Eigene Regeln können über -XX:FlightRecorderOptions ergänzt oder die Schwärzung auch komplett deaktiviert werden. Aber damit folgt auch dieses Feature dem Prinzip sicherer Defaults: Für den Normalfall muss der Benutzer nichts konfigurieren.
Kleinere API-Erweiterungen
Abseits der JEPs gibt es wieder viele kleinere Erweiterungen der Standardbibliothek. Diese finden sich in den Release Notes [3] oder können im Java Almanac [4] nachvollzogen werden. Die Klasse String wurde um eine Methode int encodedLength(Charset charset) ergänzt. Damit lässt sich bestimmen, wie viele Bytes ein String in einer bestimmten Kodierung benötigt. Bisher musste man dafür typischerweise tatsächlich kodieren und erzeugte dadurch ein unnötiges byte-Array. Passend dazu erhalten auch MemorySegment und SegmentAllocator aus dem FFM API neue Methoden, um Strings mit bekannter Länge effizient zwischen Java und Foreign Memory zu übertragen.
Die Klassen Math und StrictMath wurden um die inversen hyperbolischen Funktionen Math.asinh(x), Math.acosh(x) und Math.atanh(x) erweitert, außerdem kann BigDecimal nun direkt eine n-te Wurzel berechnen:
BigDecimal result = value.rootn(3, MathContext.DECIMAL128);
Damit müssen solche Operationen nicht mehr selbst implementiert beziehungsweise über externe Bibliotheken integriert werden.
Eine kleine, aber im Alltag möglicherweise sichtbare Änderung betrifft die vordefinierten DateTimeFormatter. Sie akzeptieren nun auch die von ISO 8601 erlaubten kurzen UTC-Offsets wie 2026-08-22T20:15+02 statt zwingend 2026-08-22T20:15+02:00 zu verlangen. Anwendungen, die externe ISO-8601-Daten verarbeiten, müssen damit weniger häufig eigene Formatter definieren.
Und es wird auch weiterhin aufgeräumt. Während mit Java 26 endgültig das Applet API weggefallen ist, gibt es in Java 27 einen weiteren Schritt bei der Entfernung von finalize. Seit Java 18 wird mit dem JEP 421 am vollständigen Abschied von Finalization gearbeitet, nachdem dieser Mechanismus bereits in Java 9 deprecated und in Java 11 der Inhalt der finalize-Methoden entfernt wurde. Jetzt wurde mit ThreadPoolExecutor.finalize() eine weitere verbliebene Finalizer-Methode aus der Standardbibliothek gelöscht. Normaler Anwendungscode bemerkt davon nichts. Wer von ThreadPoolExecutor abgeleitet und explizit super.finalize() aufgerufen hat, wird nun mit Java 27 allerdings einen Fehler bekommen. Denn der Aufruf fällt nun auf Object.finalize() zurück, dessen Signatur aber ein Throwable wirft.
Wiedervorgelegte Preview- und Inkubator-Features
Von den neun JEPs in Java 27 sind fünf keine wirklich neuen Features. Vier Preview-Funktionen und ein Inkubator-API wurden erneut vorgelegt. Während es bei Lazy Constants, Structured Concurrency und dem PEM API tatsächlich noch Änderungen gab, sind Primitive Patterns und das Vector API aber praktisch unverändert geblieben.
Lazy Constants – dritte Preview
Lazy Constants wurden in Java 25 zunächst unter dem Namen Stable Values eingeführt und mit Java 26 erheblich vereinfacht und umbenannt. Sie ermöglichen Werte, die erst beim ersten Zugriff berechnet werden sollen, danach aber dauerhaft unveränderlich bleiben. Bisher werden static final-Felder im Rahmen der Klasseninitialisierung berechnet. Das ist effizient – solange die Konstanten auch tatsächlich benötigt werden. In vielen realen Anwendungen existieren jedoch komplexe oder umfangreiche statische Strukturen, die möglicherweise nie oder nur selten verwendet werden. Die teure Initialisierung von Patterns (reguläre Ausdrücke) ist ein Beispiel:
static final Map<String, Pattern> PATTERNS = Map.of(
"email", Pattern.compile("..."),
"phone", Pattern.compile("..."),
"zip", Pattern.compile("...")
);
Beim Laden der Klasse werden alle regulären Ausdrücke kompiliert – unabhängig davon, ob sie im konkreten Anwendungslauf überhaupt benötigt werden. Lazy Constants erlauben es, solche Werte erst beim ersten tatsächlichen Zugriff zu initialisieren. Die JVM übernimmt dabei die korrekte, Thread-sichere Initialisierung, ohne dass Entwickler auf Double-checked Locking oder ähnliche Konstruktionen zurückgreifen müssen. Das reduziert unnötige Objektallokationen und aufwändige Initialisierungsarbeit beim Laden der Klassen und verkürzt insbesondere bei großen frameworkbasierten Anwendungen die Start-up-Zeiten. Gerade in Microservices mit vielen Klassen und Konfigurationsobjekten kann sich das messbar auswirken. Listing 1 zeigt ein einfaches Beispiel für einen Logger.
Listing 1: LazyConstants im Einsatz
private final LazyConstant<Logger> logger =
LazyConstant.of(() ->
Logger.create(OrderController.class));
void submitOrder(User user, List<Product> products) {
logger.get().info("order started");
// ...
logger.get().info("order submitted");
}
Die Berechnung erfolgt höchstens einmal und threadsicher. Da die JVM anschließend weiß, dass sich der Wert nicht mehr verändern kann, kann sie Zugriffe ähnlich optimieren wie bei normalen Konstanten. Im JEP 531 gab es jetzt zwei Änderungen. Die Methoden isInitialized() und orElse(…) wurden entfernt. Sie erlaubten dem Benutzer, den Initialisierungszustand zu beobachten und das Programmverhalten davon abhängig zu machen. Genau das widerspricht der Abstraktion: Ob eine Lazy Constant bereits berechnet wurde, soll ein Implementierungsdetail bleiben. Neu ist dafür Set.ofLazy(…), ein Beispiel zeigt Listing 2. Ob ein Element tatsächlich zum Set gehört, wird erst beim ersten entsprechenden Zugriff bestimmt und danach gespeichert. Nach List.ofLazy() und Map.ofLazy() gibt es damit für alle drei grundlegenden Collection-Typen eine Lazy-Variante.
Listing 2: Lazy Set mit verzögerter Initialisierung der Werte
enum Option { VERBOSE, DRY_RUN, STRICT }
// Return true when the given Option is enabled
boolean isEnabled(Option option) {
IO.println(String.format("isEnabled(%s) ...", option.name()));
// could be read from a configuration file (expensive)
if (option == Option.DRY_RUN) {
return false; // DRY_RUN is disabled
}
return true;
}
// Lazily initialized Set of Options
final Set<Option> OPTIONS =
Set.ofLazy(EnumSet.allOf(Option.class), this::isEnabled);
void main() {
IO.println("started ...");
if (OPTIONS.contains(Option.DRY_RUN)) {
IO.println("dry run mode enabled");
}
if (OPTIONS.contains(Option.STRICT)) {
IO.println("strict mode enabled");
}
// Actual processing logic
}
Primitive Types in Patterns – fünfte Preview
JEP 532 erweitert Pattern Matching auf primitive Typen. Primitive Werte können dadurch unter anderem als Type Patterns in switch, bei instanceof und innerhalb von Record Patterns auftreten. Hier ein einfaches Beispiel.
String category = switch (flights) {
case 0 -> "none";
case int i when i >= 100 -> "frequent flyer";
case int i -> "regular";
};
Mit Java 21 begann ein fundamentaler Wandel in der Sprache: Pattern Matching trat an die Stelle vieler klassischer instanceof-Checks und komplexer switch-Statements. Mit Records und Sealed Classes lassen sich algebraische Datentypen nutzen und damit invalide Zustände innerhalb der Daten vermeiden. Type Patterns, Pattern Matching in switch, Record Patterns und Unnamed Patterns haben ihr Potential bereits entfaltet. Ideen für weitere Typen (Array Patterns, Deconstructor/Factory Patterns, …) stehen schon in den Startlöchern. Aktuell wird allerdings nur an den im OpenJDK 23 eingeführten Primitive Type Patterns gearbeitet. Sie wurden jetzt als fünfte Preview (JEP 532) ohne Änderungen zum letzten Release erneut herausgebracht. Es soll weiteres Feedback gesammelt werden, bevor sie finalisiert werden.
Beim Pattern Matching geht es darum, bestehende Strukturen mit Mustern abzugleichen, um komplizierte Fallunterscheidungen effizient und wartbar implementieren zu können. Ein Pattern kombiniert dabei ein Prädikat, das auf die Zielstruktur passt, mit einer Menge von Variablen innerhalb dieses Musters. Bei einem Treffer werden diesen Variablen die entsprechenden Inhalte zugewiesen und damit aus der Zielstruktur extrahiert. Die Intention des Pattern Matching ist die Destrukturierung von Datenobjekten, also das Aufspalten in die Bestandteile und das Zuweisen in einzelne Variablen zur weiteren Bearbeitung.
Mit instanceof und switch können wir also überprüfen, ob ein Objekt von einem bestimmten Typ ist, und wenn ja, dieses Objekt einer Variable dieses Typs zuweisen und diese Variable in dem folgenden Programmpfad benutzen. Das funktionierte bisher aber nur mit Objekten und ließ sich nicht mit primitiven Datentypen kombinieren. Einzig im switch ließen sich Variablen der primitiven Typen byte, short, char und int bereits gegen Konstanten matchen. Sie konnten dort sogar mit den neueren Type Patterns kombiniert werden.
JEP 532 und seine Vorgänger verbessern nun die Typprüfung, Performance und Lesbarkeit in Java und machen Pattern Matching konsistenter, indem sie primitive Typen direkt unterstützen. Das reduziert überflüssiges Autoboxing und vereinfacht den Umgang mit primitiven Datentypen in switch-Anweisungen. Wie am Beispiel in Listing 3 zu sehen ist, können Entwickler in Zukunft ganz einfach prüfen, ob ein ganzzahliger Wert in den Wertebereich eines Bytes passt.
Listing 3: Wertebereichsprüfung von primitiven Datentyp
private static String checkByte(int value) {
if (value instanceof byte b) {
return "byte b = " + b;
} else {
return "kein byte: " + value;
}
}
System.out.println(checkByte(127)); // b = 127
System.out.println(checkByte(128)); // kein byte: 128
Wichtige Themen beim Pattern Matching sind die Exhaustiveness-Prüfung und die sogenannte Dominanzregel: Letztere stellt sicher, dass kein case-Label unerreichbar wird, weil ein früheres Pattern es bereits abdeckt. Bei Primitive Patterns kommen hier zusätzliche Überlegungen ins Spiel: Da es bei primitiven Typen keine Vererbung gibt, entscheidet der Compiler anhand von exakten und verlustfreien Konvertierungen, welches Pattern Vorrang hat.
Mit der Erweiterung um Primitive Type Patterns wird Pattern Matching konsistent auf die gesamte Sprache ausgedehnt. Entwickler können direkt mit primitiven Werten in Patterns arbeiten, redundanten Code reduzieren und komplexe Auswertungen klarer und kompakter formulieren. Das ist nicht nur syntaktischer Zucker, es trägt zur Ausdrucksstärke und Lesbarkeit bei, reduziert Fehlerquellen und schafft eine einheitlichere Sprache – ein wichtiger Schritt auf dem Weg zu moderner, deklarativer Programmierung in Java.
Structured Concurrency – siebte Preview
Dieses API behandelt eine Gruppe zusammengehöriger nebenläufiger Tasks als eine gemeinsame Arbeitseinheit (Listing 4) und ist damit ein wichtiger Schlüssel zur effizienten Verwendung von Virtual Threads. Mit ihrer Einführung in Java 21 wurde Nebenläufigkeit in Java radikal vereinfacht. Doch Threads allein lösen noch kein strukturelles Problem. Wie orchestriert man vielmehr mehrere parallele Aufgaben so, dass Fehler, Abbrüche und Lebenszyklen sauber kontrolliert bleiben? Genau hier setzt Structured Concurrency an. Im OpenJDK 25 gab es einige größere Änderungen an dem API, die jetzt weiter getestet werden sollen und zu denen man Feedback sammeln möchte.
Listing 4: StructuredTaskScope für zwei Teilaufgaben
try (var scope = StructuredTaskScope.open()) {
var user = scope.fork(this::loadUser);
var orders = scope.fork(this::loadOrders);
scope.join();
return new Result(user.get(), orders.get());
}
In der klassischen Nebenläufigkeit wird typischerweise mit Futures oder Executor Services gearbeitet. Die Probleme dabei sind:
-
Die Fehlerbehandlung ist fragmentiert.
-
Wenn eine Aufgabe fehlschlägt, laufen andere möglicherweise weiter.
-
Das Abbrechen von Tasks ist kompliziert.
-
Die Lebensdauer von Tasks ist nicht klar an einen Scope gebunden.
-
Thread-Leaks sind möglich.
Nebenläufigkeit ist so strukturell schwer kontrollierbar. Structured Concurrency behandelt die Nebenläufigkeit wie eine Blockstruktur und verfolgt dabei ein einfaches Prinzip: Nebenläufige Aufgaben gehören in einen gemeinsamen Lebenszyklus – genau wie Methodenaufrufe in einem Block.
Anstatt lose Futures zu erzeugen, werden parallele Tasks in einem Scope gestartet und gemeinsam verwaltet. Der Aufruf von join() wartet strukturiert darauf, dass alle Aufgaben abgeschlossen sind. Falls ein Task fehlschlägt, werden die anderen automatisch abgebrochen. Und nach Verlassen des try-Blocks ist garantiert, dass kein Task weiterlebt. Nebenläufigkeit wird damit lexikalisch strukturiert, ähnlich wie Ressourcen mit try-with-resources.
Seine volle Stärke entfaltet Structured Concurrency in Kombination mit Virtual Threads. Jeder Task kann in einem Virtual Thread laufen und das ohne Thread-Pool-Tuning sowie ohne komplexes Ressourcenmanagement. Das erlaubt ein Programmiermodell, das synchron aussieht, aber massiv parallel skaliert. Und das ohne Callback-Hölle oder den Overhead von reaktiver Programmierung. Im Rahmen moderner Backendarchitekturen passt das hervorragend für:
-
die Aggregation mehrerer Services
-
parallele Datenbank- oder API-Calls
-
Fan-out/Fan-in-Muster
-
Resilienz-Patterns
Gerade in Cloud-Umgebungen mit I/O-lastigen Workloads wird dieses Modell sehr relevant. Und es ist mehr als syntaktischer Komfort. Es verändert das Denkmodell. Vorher haben wir Nebenläufigkeit als Sammlung unabhängiger Futures betrachtet, jetzt ist es strukturierter Teil eines Kontrollflusses.
Zwischen den beiden Extremen – Low-Level-Threads mit komplexer Verwaltung und reaktiven Frameworks mit steiler Lernkurve – ermöglicht Structured Concurrency einen imperativen Stil mit klarer Lebensdauer, robuster Fehlerbehandlung und hoher Skalierbarkeit. In Kombination mit Virtual Threads entsteht damit ein Nebenläufigkeitsmodell, das sowohl gut verständlich/wartbar als auch leistungsfähig ist.
In Java 27 wurde insbesondere die Fehlerbehandlung überarbeitet. StructuredTaskScope und Joiner berücksichtigen nun zusätzlich den Typ der Exception, die join() werfen kann. Dadurch können Joiner genauer ausdrücken, wie Fehler der einzelnen Subtasks in einen Fehler des gesamten Scopes übersetzt werden. Neu ist außerdem awaitAllSuccessfulOrThrow(). Diese Strategie eignet sich gut, wenn die Subtasks unterschiedliche Ergebnistypen besitzen. join() wartet darauf, dass alle erfolgreich sind, anschließend werden die Ergebnisse über die zuvor zurückgegebenen Subtask-Objekte abgefragt.
Daneben wurde das Timeout API aufgeräumt. onTimeout() heißt nun timeout(), und mit CancelledByTimeoutException gibt es einen neuen Exception-Typ für den Abbruch aufgrund eines Zeitlimits. Das bisherige awaitAll() entfällt. Außerdem gibt es nun mit StructuredTaskScope.open(configOperator) die Möglichkeit, einen Scope zu konfigurieren, ohne einen eigenen Joiner anzugeben. Die Grundidee von Structured Concurrency ist längst stabil. Die ungewöhnlich hohe Zahl von inzwischen sieben Preview-Runden entsteht vor allem dadurch, dass im OpenJDK weiterhin an der exakten Form des API gearbeitet wird.
PEM API – dritte Preview
Sicherheit ist in modernen Systemen kein optionales Zusatzfeature, sondern grundlegende Infrastruktur. Zertifikate, Schlüsselpaare, Signaturen und TLS-Konfigurationen gehören zum Alltag – insbesondere in Cloud- und API-zentrierten Architekturen. Ein relevanter Baustein ist das PEM API, das nun zum dritten Mal als Preview dabei ist. PEM-Dateien enthalten beispielsweise Zertifikate und Schlüssel:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/kRGOL7wCPTN4KJ2ppeSt5UYB6u
cPjjuKDtFTXbguOIFDdZ65O/8HTUqS/sVzRF+dg7H3/tkQ/36KdtuADbwQ==
-----END PUBLIC KEY-----
Obwohl dieses Format allgegenwärtig ist – insbesondere im TLS- und Cloud-Umfeld –, war die direkte Unterstützung im JDK bislang begrenzt. Entwickler mussten häufig auf Drittbibliotheken zurückgreifen oder eigene Parsing-Lösungen implementieren.
Mit JEP 538 und seinen Vorgängern wird jetzt ein natives API bereitgestellt, um PEM-kodierte Objekte direkt zu lesen und zu schreiben. Das API abstrahiert dabei das Header-/Footer-Handling, das Base64-Decoding und die Formatvalidierung. Gerade in modernen Deployment-Szenarien sind PEM-Dateien allgegenwärtig, z. B. in Kubernetes Secrets, Cloud-Zertifikaten, mTLS-Konfigurationen, ACME-/Let’s-Encrypt-Integrationen und API-Gateways. Durch die native Unterstützung werden Boilerplate-Code, Fehlerquellen beim Parsen und Validierung sowie Abhängigkeiten von Drittbibliotheken reduziert. Gerade in sicherheitskritischen Systemen ist es ein Vorteil, wenn kryptografische Operationen möglichst nahe an der Standardplattform bleiben.
In Java 27 wurde insbesondere die Abstraktion für kodierbare Objekte verallgemeinert. Das bisherige DEREncodable wurde durch BinaryEncodable ersetzt. Entsprechend verwenden beispielsweise X509Certificate, KeyPair, PKCS8EncodedKeySpec und EncryptedPrivateKeyInfo nun die allgemeinere Schnittstelle. Auch PEMEncoder akzeptiert BinaryEncodable. Der Grund ist konzeptionell sinnvoll. PEM ist zwar häufig die Base64-Textrepräsentation eines DER-kodierten Objekts, das API soll aber nicht unnötig auf DER als einzig mögliche binäre Repräsentation festgelegt sein.
Auch PEMDecoder wurde weiter verfeinert. Unter anderem wurde aus withFactory(Provider) die präzisere Methode withFactoriesOf(Provider). Und das bisher als Record modellierte PEM wird zu einer normalen Klasse. Hinzu kommt mit CryptoException ein gemeinsamer Exception-Typ für entsprechende Operationen.
Stay tuned
Regelmäßig News zur Konferenz und der JAX-Community erhalten
Vector API – zwölfte Inkubator-Runde
Das Vector API hält einen bemerkenswerten Rekord: Java 27 bringt bereits seine zwölfte Inkubatorversion. Es ermöglicht explizite SIMD-Berechnungen (Single Instruction, Multiple Data), die die JVM beispielsweise auf AVX- oder NEON-Instruktionen der jeweiligen CPU abbilden kann (Listing 5). Dabei werden mehrere float-Werte gleichzeitig verarbeitet – je nach CPU z. B. 128 oder 256 Bit breit. SIMD ist entscheidend für numerische Simulationen, Signalverarbeitung, Bildverarbeitung und Machine Learning, dabei insbesondere für Embedding-Berechnungen und Vektorähnlichkeitsvergleiche. Bislang war Java hier oft auf native C-Bibliotheken (über JNI) angewiesen. Das Vector API bringt diese Fähigkeiten direkt in die JVM – portabel und optimierbar durch den Just-in-Time-(JIT-)Compiler.
Listing 5: Gegenüberstellung mit und ohne Vector API
// ohne Vector API
for (int i = 0; i < a.length; i++) {
c[i] = a[i] + b[i];
}
// mit Vector API
import jdk.incubator.vector.*;
var species = FloatVector.SPECIES_PREFERRED;
for (int i = 0; i < a.length; i += species.length()) {
var va = FloatVector.fromArray(species, a, i);
var vb = FloatVector.fromArray(species, b, i);
var vc = va.add(vb);
vc.intoArray(c, i);
}
Für Java 27 gibt es allerdings keine wesentlichen API-Änderungen. Die erneute Inkubation ist vor allem eine Folge der Abhängigkeit von Project Valhalla. Denn Vektoren sind typische Kandidaten für Value Classes: Sie sollen sich wie Werte verhalten, keine relevante Objektidentität besitzen und möglichst ohne klassischen Objekt-Overhead verarbeitet werden. OpenJDK möchte deshalb vermeiden, das heutige API zu standardisieren und es nach der Einführung entsprechender Valhalla-Funktionen wieder grundlegend ändern zu müssen. Da mit Java 28 im März 2027 ein erster Stand der Value Classes Einzug ins OpenJDK erhält, könnte auch das Vector API bald in den Preview- und anschließend in den finalen Status wechseln.
Fazit und Ausblick
Java 27 ist kein Release der großen Sprachänderungen. Gerade die wichtigsten Neuerungen funktionieren vielmehr weitgehend unterhalb des Anwendungscodes. Compact Object Headers reduzieren automatisch den Speicherbedarf, G1 wird zu einem verlässlicheren Default über unterschiedliche Deployment-Umgebungen hinweg, TLS kann ohne Codeänderungen Post-Quantum-Verfahren aushandeln und JFR schützt sensible Informationen künftig standardmäßig. Daneben zeigen die Wiedervorlagen ein interessantes Bild: Primitive Patterns und das Vector API verändern sich kaum noch, während insbesondere Structured Concurrency, das PEM API und Lazy Constants weiterhin API-seitig nachgeschärft werden. Alle weiteren kleineren Neuerungen ohne eigenen JEP finden sich in den Release Notes [3]. Änderungen am JDK beziehungsweise an der Java-Klassenbibliothek lassen sich zudem übersichtlich im Java Almanac [4] nachvollziehen.
Damit steht Java 27 vor allem für bessere Defaults, Sicherheit und Konsolidierung. Viele der Verbesserungen sind nicht spektakulär, können bestehende Anwendungen aber bereits allein durch den Wechsel auf die neue JVM effizienter oder sicherer machen. Und nächstes Jahr kommen mit Project Valhalla und JEP 401 die Value Classes als nächste sehr große Änderung am Typsystem von Java. Sie versprechen:
-
Objekte ohne Identität
-
kein Objekt-Header
-
kompakteres Speicherlayout
-
bessere Cache-Lokalität
-
deutlich geringeren GC-Druck
-
explizitere Null-Semantik
Damit verschwimmt die historische Grenze zwischen primitiven Typen und Referenztypen immer weiter. Domänenmodelle könnten künftig aus echten Werttypen bestehen – ohne Performancenachteile durch den Overhead der Objekt-Header. Für viele Anwendungsfälle ist das hochrelevant, z. B. numerische Simulationen, Finanzberechnungen, Embedding- und Vektorverarbeitung, große Datenstrukturen und KI-nahe Workloads. Java wird damit nicht nur syntaktisch, sondern auch speichertechnisch modernisiert.
Parallel dazu verschiebt sich das Sicherheits- und Integritätsmodell der Plattform. Unter dem Leitgedanken „Integrity by Default“ wird die Möglichkeit eingeschränkt, Objektinvarianten über Reflection oder Deserialisierung zu umgehen. Gerade in großen Architekturen, in denen Invarianten ein zentrales Designinstrument sind, stärkt das die Verlässlichkeit des Codes. Und für eine moderne Serialisierung werden Deconstruction Patterns für eine gleichberechtigte Unterstützung von Konstruktion und Dekonstruktion sorgen. Wenn ein Objekt konstruiert werden kann, sollte es auch strukturell zerlegt werden können – als First-class-Sprachkonzept. Konzeptionell könnte das wie in Listing 6 aussehen (Vorsicht, das ist nur ein erster Vorschlag!).
Listing 6: Mögliche Syntax für Dekonstruktoren
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public pattern Point(int x, int y) {
x = this.x;
y = this.y;
}
}
// im Pattern Matching:
if (obj instanceof Point(var x, var y)) {
System.out.println(x + "," + y);
}
Konstruktion und Dekonstruktion wären damit symmetrisch und bildeten ein mächtiges Konzept für klarere Geschäftslogik, bessere Pattern-basierte Auswertungen, weniger Boilerplate und saubere Serialisierung. Sogenannte Carrier Classes waren Anfang des Jahres als Grundlage für das neue Serialisierungsmodell in der Diskussion [5]. Statt Konstruktoren zu umgehen oder final-Felder per Reflection zu manipulieren, würde Serialisierung explizit über definierte Deconstruction-Patterns laufen. Das würde die Invarianten respektieren, die Versionierung vereinfachen und Framework-Abhängigkeiten reduzieren, etwa zu Jackson bei der JSON-Serialisierung. Das JDK und die Sprache Java würden damit Lücken schließen, die seit Jahrzehnten von Drittbibliotheken gefüllt waren.
Setzt man all diese Entwicklungen zusammen, ergibt sich ein klarer Trend: Java modernisiert nicht nur einzelne APIs – es überarbeitet sein Fundament: Value Classes verändern das Speicherlayout, Integrity by Default stärkt das Objektmodell, Deconstruction Patterns erweitern das Sprachkonzept und Vector API sowie GC-Optimierungen adressieren Hardwareeffizienz. Start-up-Optimierungen machen Java zudem Cloud-tauglicher. Die Ideen für die nächsten Funktionen gehen den JDK-Entwicklern also nicht aus. Wer sich vorab über weitere mögliche Zukunftsthemen informieren möchte, kann sich schon mal im JEP-Index unter „Draft and submitted JEPs“ [6] umschauen. Die Zukunft für Java-Entwickler wird spannend bleiben.
Links & Literatur
[1] https://openjdk.java.net/projects/jdk/27/
[2] https://mail.openjdk.org/archives/list/[email protected]/thread/AIA3O3LHFZ6T7TIPH7KZT4WS4B6U72U5/
[3] https://jdk.java.net/27/release-notes
[4] https://javaalmanac.io/jdk/27/apidiff/26/
[5] https://mail.openjdk.org/pipermail/amber-spec-experts/2026-January/004307.html
Author
🔍 FAQ
1. Was ist neu in Java 27?
Java 27 bringt neun JEPs. Zu den wichtigsten Neuerungen gehören Compact Object Headers als Standard, G1 als Default Garbage Collector in allen Umgebungen, ein hybrider Post-Quantum-Schlüsselaustausch für TLS 1.3 und die automatische Schwärzung sensibler Daten im Java Flight Recorder. Weitere Features wie Structured Concurrency, Lazy Constants und Primitive Type Patterns befinden sich weiterhin im Preview-Status.
2. Wann wurde Java 27 veröffentlicht?
Java 27 wurde am 15. September 2026 veröffentlicht.
3. Ist Java 27 eine LTS-Version?
Nein. Java 27 ist kein Long-Term-Support-Release. Java 25 ist die aktuelle LTS-Version. Die nächste LTS-Version soll Java 29 im September 2027 werden.
4. Muss Anwendungscode für Java 27 angepasst werden?
Viele der wichtigsten Neuerungen funktionieren ohne Änderungen am Anwendungscode. Dazu gehören Compact Object Headers, G1 als Standard-Garbage-Collector sowie die Unterstützung hybrider Post-Quantum-Verfahren in TLS 1.3. Bei JVM-nahen Tools, Agents, Preview-Features und bestimmten entfernten APIs kann eine Prüfung beziehungsweise Anpassung jedoch notwendig sein.
5. Welche Preview-Features enthält Java 27?
Java 27 enthält unter anderem Lazy Constants, Primitive Types in Patterns, Structured Concurrency und das PEM API als Preview-Features. Das Vector API befindet sich weiterhin im Inkubator-Status.





