Dieser Blogbeitrag gibt einen Überblick über die neuen Features von Java 24.
Am 18.03.2025 ist Java 24 erschienen. Passenderweise enthält das Release auch genau 24 JEPs. Hier ein Überblick über die neuen Features.
Die Preview-Features
Eine neue Preview ist dabei:
JEP-478 – Key Derivation Function API (Preview)
Key Derivation Functions (KDFs) nutzen kryptographische Eingaben wie Basisschlüssel, Salt und Pseudozufallsfunktionen, um neue, starke Schlüssel zu generieren. Sie erlauben die reproduzierbare und sichere Erzeugung verschiedener Schlüssel, ähnlich wie beim Password-Hashing. KDFs extrahieren oder erweitern Schlüsselmaterial, indem sie einen keyed Hash mit zusätzlicher Entropie kombinieren. Angesichts der steigenden Bedrohung durch Quantencomputing wird post-quantensichere Kryptographie wichtiger. Die Java-Plattform strebt an, mit Hybrid Public Key Encryption (HPKE) und einer neuen KDF-API den Übergang zu quantensicheren Algorithmen zu erleichtern. Dies fördert Interoperabilität, unterstützt Standards wie PKCS#11 und ermöglicht modernere Password-Hashing-Verfahren (z.B. Argon2). Dadurch wird die Sicherheit und Flexibilität zukünftiger Anwendungen deutlich erhöht.
Codebeispiel aus dem JEP:
// Create a KDF object for the specified algorithm
KDF hkdf = KDF.getInstance(„HKDF-SHA256“);
// Create an ExtractExpand parameter specification
AlgorithmParameterSpec params =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial)
.addSalt(salt).thenExpand(info, 32);
// Derive a 32-byte AES key
SecretKey key = hkdf.deriveKey(„AES“, params);
// Additional deriveKey calls can be made with the same KDF object
-> zum JEP-478
Enthalten sind außerdem wieder etliche Preview-Features, die auch schon in den vorangehenden Versionen enthalten waren, und über die ich bereits in früheren Blogbeiträgen berichtet hatte:
JEP-487 – Scoped Values (Fourth Preview): Eine leichtgewichtige Alternative zu ThreadLocal. Die Methoden callWhere und runWhere wurden entfernt, um eine komplett flüssige („fluent“) API zu ermöglichen.
JEP-488 – Primitive Types in Patterns, instanceof and switch (Second Preview): keine Änderungen im Vergleich zur First Preview.
JEP-489 – Vector API (Ninth Incubator): eine API zum Rechnen mit Vektoren, mit einer Reihe von Anpassungen. Die API bleibt im Incubator-Status, bis die Features von Project Valhalla (Ergänzung von Java um Value Objects) als Preview-Features verfügbar werden.
JEP-492 – Flexible Constructor Bodies (Third Preview): Dieses Feature ermöglicht es, Statements in Konstruktoren unter gewissen Voraussetzungen vor den Aufruf von super() oder this() zu stellen. Keine nennenswerten Änderungen seit der Second Preview.
JEP-494 – Module Import Declarations (Second Preview): erlaubt den Import aller Packages eines Java-Moduls mit import module <module-name>.
JEP-495 – Simple Source Files and Instance Main Methods (Fourth Preview): soll Anfängern den Einstieg in Java erleichtern, indem das Schreiben von kleinen Programmen vereinfacht wird. Außer Anpassungen der Terminologien und des Titels finden sich keine Änderungen gegenüber der Third Preview.
JEP-499 – Structured Concurrency (Fourth Preview): eine API für besser lesbaren, wartbaren und zuverlässigen nebenläufigen Code. Keine Änderungen zur vorangegangenen dritten Preview.
Experimentelle Features
Neben den Previews gibt es auch zwei experimentelle Features:
JEP-404 – Generational Shenandoah (Experimental)
Der Shenandoah Garbage Collector wird um einen experimentellen generationalen Modus erweitert, der den Speicherverbrauch, die CPU-/Energieeffizienz und die Resilienz bei Lastspitzen verbessern soll – ohne die bewährte nicht-generationale Variante zu ersetzen. In diesem Ansatz wird der Java-Heap in zwei Generationen unterteilt: eine junge Generation, in der die meisten kurzlebigen Objekte gesammelt werden, und eine alte Generation, die langfristig erhaltene Objekte verwaltet. Ziel ist es, den Speicherbedarf zu senken und gleichzeitig niedrige GC-Pausen beizubehalten (-> zum JEP-404).
JEP-450 – Compact Object Headers (Experimental)
Objekt-Header, die in der HotSpot-JVM derzeit zwischen 96 und 128 Bit benötigen, sollen komprimiert werden, so dass ihre Größe nur noch 64 Bit beträgt. Es wird erwartet, dass der Speicherverbrauch von Java-Anwendungen sich dadurch um 10-20% verringert (-> zum JEP-450).
Die neuen Features
Dies sind die offiziellen Neuerungen, die im JDK 24 enthalten sind.
JEP-472 – Prepare to Restrict the Use of JNI
Die JVM gibt nun Warnungen aus, wenn eine Anwendung mittels Java Native Interface (JNI) auf nativen Code zugreift. Bei der Foreign Function & Memory API (FFM), die ebenfalls das Ausführen von nativem Code erlaubt, ist dies bereits der Fall. Längerfristiges Ziel ist es, in späteren JDK-Versionen den Zugriff auf nativen Code nur noch zu erlauben wenn dies anhand eines Kommandozeilenparameters explizit aktiviert wird, da die Interaktion mit nativem Code riskant ist, welche zudem in einem Großteil der Java-Anwendungen gar nicht benötigt wird (-> zum JEP-472).
JEP-475 – Late Barrier Expansion for G1
Die Barrieren-Implementierung des G1-Garbage-Collectors, die Informationen über Speicherzugriffe der Anwendung aufzeichnet, wird vereinfacht. Hierbei werden entsprechende G1-Funktionalitäten von den Interna des C2-Just-in-time-Compilers entkoppelt. Dies versetzt die GC-Entwickler in die Lage, den Overhead von G1 weiter zu optimieren und zu reduzieren, ohne dass sie dafür ein tiefes Verständnis des C2-Compilers haben müssen (-> zum JEP-475).
JEP-479 – Remove the Windows 32-bit x86 Port
Im Oktober 2025 wird der Support für Windows 10 eingestellt, der letzten Windows-Version, die noch als 32-Bit-Variante verfügbar ist. Zudem fällt die Implementierung von Virtuellen Threads für 32 Bit auf Kernel-Threads zurück, wordurch der Mehrwert von Project Loom quasi nicht vorhanden ist. Daher wird die Unterstützung der 32-Bit-Portierung des JDK für Windows zur Version 24 eingestellt (-> zum JEP-479).
JEP-501 – Deprecate the 32-bit x86 Port for Removal
Die Kosten für die Pflege der 32-Bit-Portierung für Linux übersteigen die Vorteile bei weitem. Durch die Markierung als Deprecated und Entfernung in einem späteren Release werden Entwicklerressourcen frei, um die Entwicklung neuer Features und Verbesserungen zu beschleunigen. Das Entfernen der 32-Bit-Portierung ist für JDK 25 angedacht.
JEP-483 – Ahead-of-Time Class Loading & Linking
Diese Funktion baut auf dem Class Data Sharing (CDS) der HotSpot JVM auf und hat zum Ziel, den Start von Java-Anwendungen noch weiter zu beschleunigen.
Beim Start eines Java-Programms müssen zuerst die verwendeten Klassen von der Platte gelesen, geparst und verlinkt werden. Dies passiert bei jedem Start erneut, und benötigt immer eine gewisse Zeit. Ab Java 24 bietet die JVM nun die Möglichkeit, einen ahead-of-time-Cache zu erstellen, in dem die geladenen und gelinkten Klassen der Anwendung abgelegt werden, und der anschließend beim Starten der Anwendung verwendet werden kann.
Das Erzeugen des Caches erfolgt in 2 Schritten (dieser Prozess soll in Zukunft noch optimiert werden). Zuerst wird eine AOTConfiguration erstellt, in der die von der Anwendung genutzten Klassen gelistet sind. Dazu startet man die Anwendung mit zwei speziellen Kommandozeilenparametern:
$ java -XX:AOTMode=record -XX:AOTConfiguration=myapp.aotconf -cp myapp.jar de.doubleSlash.MyApp
Basierend auf der AOTConfiguration in der Datei myapp.aotconf wird nun der Cache erzeugt:
$ java -XX:AOTMode=create -XX:AOTConfiguration=myapp.aotconf
-XX:AOTCache=myapp.aot -cp myapp.jar
In diesem Schritt wird die Anwendung nicht ausgeführt. Es wird lediglich der Cache erzeugt und in der Datei myapp.aot abgelegt.
Dieser kann nun beim Starten der Anwendung wie folgt verwendet werden:
$ java -XX:AOTCache=myapp.aot -cp myapp.jar de.doubleSlash.MyApp
Beim Erzeugen der AOTConfiguration sollte man sicherstellen dass alle von der Anwendung benötigten Klassen in der Konfigurationsdatei landen, um den bestmöglichen Effizienzgewinn zu erreichen. Insbesondere bei dynamisch (z.B. via Reflection) geladenen Klassen ist dies nicht unbedingt immer der Fall. Im Gegensatz zur Ahead-of-Time-Kompilierung (etwa per GraalVM) ist es allerdings nicht fatal, wenn Klassen im AOT-Cache fehlen; diese werden dann einfach beim Anwendungsstart auf die übliche Weise geladen und verlinkt. Nur greift der Vorteil des Caches für diese Klassen dann nicht.
Performancemessungen ergaben eine Verkürzung der Startzeit um mehr oder weniger die Hälfte. Für mehr Details zu diesem Feature empfehle ich den JEP, sowie den lesenswerten Blogbeitrag von Gunnar Morling zu diesem Thema, der den Start eines Kafka-Servers mit dem AOT-Cache um satte 59% beschleunigen konnte (-> zum JEP-483).
JEP-484 – Class-File API
Im Java-Ökosystem werden Klassendateien häufig geparst, generiert und transformiert, insbesondere in Tools und Frameworks, die dynamisch Bytecode manipulieren. Hierfür kommen Bibliotheken wie ASM, BCEL und Javassist zum Einsatz – auch das JDK nutzt eine eigene Lösung. Durch neue Sprach- und JVM-Features ist das Format der Klassendateien einer ständigen Weiterentwicklung unterworfen, weshalb eine standardisierte, zukunftsfähige API notwendig ist, um Reaktionsfähigkeit und effektive Unabhängigkeit von ASM für Java zu sichern. Die zu diesem Zweck entwickelte Class-File API wurde nach zwei Previews nun final veröffentlicht (-> zum JEP-484)
JEP-485 – Stream Gatherers
Ebenfalls nach zwei Previews wurden die Stream Gatherers finalisiert und offiziell ins JDK aufgenommen. Dabei handelt es sich um einen Extension Point für die Stream-API in Form eines Interfaces namens Gatherer, sowie einer gather(…)-Methode, mit deren Hilfe Stream-Operationen implementiert werden können, die in der API selbst nicht zur Verfügung stehen.
Hier ein Beispiel für einen einfachen Gatherer, der jedes Element in einem Stream vervielfacht:
public static <T> Gatherer<T, ?, T> multiplyBy(int times) {
return Gatherer.ofSequential((state, element, downstream) -> {
for (int i = 0; i < times; i++) {
downstream.push(element);
}
return true;
});
}
Die Verwendung im Stream sieht so aus:
List<String> multiplied = Stream.of(„a“, „b“, „c“)
.gather(multiplyBy(3))
.toList();
Das Ergebnis ist eine Liste, die jedes Element aus dem ursprünglichen Stream dreimal enthält, also (a, a, a, b, b, b, c, c, c).
Eine Reihe von Gatherer-Implementierungen, wie beispielsweise windowFixed, windowSliding oder fold, sind über die Klasse Gatherers erhältlich.
Die Anwendung von windowSliding etwa sieht wie folgt aus:
Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.windowSliding(3))
.forEach(System.out::println);
Die Ausgabe dazu:
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
-> zum JEP-485
JEP-486 – Permamently Disable the Security Manager
Der Security Manager ist seit vielen Jahren nicht mehr das primäre Mittel zur Absicherung von clientseitigem Java-Code. Er wurde nur selten zur Absicherung von serverseitigem Code verwendet und ist teuer in der Wartung. Daher wurde er mit JEP-411 (2021) in Java 17 als deprecated for removal deklariert. Im nächsten Schritt wird die Spezifikation der Java-Plattform überarbeitet, so dass Entwickler ihn nicht mehr aktivieren können, und andere Plattformklassen nicht mehr auf ihn verweisen. Diese Änderung wird keine Auswirkungen auf die große Mehrheit der Anwendungen, Bibliotheken und Tools haben. Die Security-Manager-API wird in einer zukünftigen Version entfernt werden (-> zum JEP-486).
JEP-490 – ZGC: Remove the Non-Generational Mode
Der Generational Mode im ZGC Garbage Collector wurde mit JEP-439 eingeführt (JDK 21) und wird seit JEP-474 (JDK 23) standardmäßig verwendet. Um die Wartungskosten für zwei verschiedene Modi einzusparen, wird der Non-Generational Mode aus dem ZGC entfernt (-> zum JEP-490).
JEP-491 – Synchronize Virtual Threads without Pinning
Mit dem Schlüsselwort synchronized in Java kann man Stellen im Code markieren, die immer nur von einem Thread gleichzeitig ausgeführt werden dürfen, um sogenannte Race Conditions bei nebenläufiger Verarbeitung zu verhindern.
Bisher ist die Funktionalität von synchronized an Plattform-Threads gebunden, was bedeutet, dass ein Plattform-Thread so lange gebunden ist, so lange er sich in einem synchronized-Block befindet (Thread Pinning). Werden allerdings Virtual Threads genutzt, also leichtgewichtige Threads, von denen sehr viele auf einem einzigen Plattform-Thread, dem sogenannten „Träger-Thread“, laufen können, wird dies zum Problem. Häufiges Pinning über lange Zeiträume kann die Skalierbarkeit beeinträchtigen. Es kann zum „Verhungern“ von Threads oder sogar zum Deadlock führen, wenn keine virtuellen Threads ausgeführt werden können, weil alle Plattform-Threads, die dem Scheduler des JDK zur Verfügung stehen, entweder von virtuellen Threads belegt oder in der JVM blockiert sind.
Mit diesem JEP wurde die Funktionalität von synchronized so angepasst, dass sie unabhängig von den Träger-Threads funktioniert, und die o.g. Probleme nicht mehr auftreten (-> zum JEP-491)
JEP-493: Linking Run-Time Images without JMODs
Mit jlink können individuelle Laufzeit-Images erstellt werden, welche nur die von einer Anwendung unbedingt benötigten JDK-Module in Form von JMOD-Dateien enthalten, was zu einer reduzierten Größe des installierten JDK führt. Ein komplettes JDK besteht hauptsächlich aus einem Laufzeit-Image, welches das ausführbare Java-System darstellt, sowie den JMOD-Dateien, in welchen die Module des Laufzeit-Images paketiert sind. Da sämtliche Klassen, native Bibliotheken, Konfigurationsdateien und andere Ressourcen sowohl im Laufzeit-Image als auch in den JMOD-Dateien enthalten sind, sind vollständige JDK-Versionen größer und beanspruchen deswegen mehr Ressourcen als eigentlich nötig. Dieser JEP beinhaltet eine Optimierung von jlink, durch welche die Ressourcen direkt aus dem Laufzeit-Image extrahiert und somit die JMOD-Dateien weggelassen werden können. Dies kann die Größe eines installierten JDK um rund 25% reduzieren (-> zum JEP-493).
JEP-496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism
Diese Erweiterung verbessert die Sicherheit von Java-Anwendungen durch Bereitstellung einer Implementierung des quantenresistenten Modul-Gitter-basierten Schlüsselkapselungsmechanismus (Module-Lattice-Based Key-Encapsulation Mechanism / ML-KEM). KEMs werden verwendet, um symmetrische Schlüssel über unsichere Kommunikationskanäle mit Hilfe der Public-Key-Kryptographie zu sichern. ML-KEM ist so konzipiert, dass es gegen künftige Angriffe durch Quantencomputer sicher ist. Es wurde vom United States National Institute of Standards and Technology (NIST) in FIPS 203 standardisiert (-> zum JEP-496).
JEP-497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm
Hiermit wird die Sicherheit von Java-Anwendungen erhöht, indem eine Implementierung des quantenresistenten Module-Lattice-Based Digital Signature Algorithm (ML-DSA) bereitgestellt wird. Digitale Signaturen werden verwendet, um unbefugte Änderungen an Daten zu erkennen und die Identität der Unterzeichner zu authentifizieren. ML-DSA ist so konzipiert, dass es gegen künftige Angriffe durch Quantencomputer sicher ist. Es wurde vom United States National Institute of Standards and Technology (NIST) in FIPS 204 standardisiert (-> zum JEP-497).
JEP-498: Warn upon Use of Memory-Access Methods in sun.misc.Unsafe
In JEP-471 (JDK 23) wurden die Speicherzugriffsfunktionen aus der Klasse sun.misc.Unsafe als deprecated und für die Entfernung in einem späteren Release markiert. Entsprechende Funktionalitäten werden mittlerweile durch offizielle Standard-JDK-APIs bereitgestellt. Daher werden nun Warnungen zur Laufzeit ausgegeben, wenn eine Anwendung die alten Speicherzugriffsmethoden von sun.misc.Unsafe verwendet. Das Ziel ist, Entwickler darauf aufmerksam zu machen und sie zur Migration auf die neuen APIs zu bewegen, damit sun.misc.Unsafe in einem späteren Release entfernt werden kann (-> zum JEP-498).
Der Beitrag Java 24 – ein Überblick erschien zuerst auf Business -Software- und IT-Blog – Wir gestalten digitale Wertschöpfung.