
Java 25 — ce qu’il faut retenir

Article rédigé par
Achraf Hasbi
,
Practice Manager,
coach
et expert Java chez MARGO.
Java 25 : Performances, sécurité et productivité
Java 25, la dernière version LTS, continue d'améliorer les performances, la sécurité et la productivité des développeurs. Dans le cadre du rythme de release régulier d'Oracle, Java 25 introduit plusieurs fonctionnalités qui améliorent l’expressivité du langage et optimisent les performances de la JVM.
Cette version comprend 18 JEP: 12 finalisés, 4 en preview, 1 experimental et 1 incubator.
Dans cet article, nous explorerons les nouvelles fonctionnalités de Java 25, en mettant l’accent sur les JEP finalisés et leur impact sur le développement Java.
Téléchargez OpenJDK 25 ici : OpenJDK 25
JVM : Nouvelles évolutions dans Java 25
JEP 503 : Supprimer le 32-bit x86 Port
La JEP 503 propose la suppression du port x86 32 bits du JDK. Cette décision, faisant suite à sa dépréciation dans la JEP 501, vise à supprimer le code source associé et le support de build.
La principale raison de cette suppression est la lourdeur croissante de la maintenance du port x86 32 bits. Les efforts déployés pour l'adapter aux nouvelles fonctionnalités (Loom, l'API Foreign Function & Memory (FFM) et l'API Vector) se sont avérés coûteux. L'abandon et la suppression définitive de ce port permettent aux développeurs OpenJDK d'accélérer le développement de nouvelles fonctionnalités et améliorations.
JEP 514 : Ergonomie en ligne de commande anticipée
JEP 514 simplifie le processus de création de caches Ahead-Of-Time (AOT), qui accélèrent le démarrage des applications Java, en rationalisant les commandes requises pour les cas d'utilisation courants.
Avant la norme JEP 514, la création d'un cache AOT impliquait un processus en deux étapes :
- 1. Entraîner l'application pour enregistrer le comportement.
- 2. Création du cache AOT à partir des données enregistrées.
Ce processus était fastidieux et laissait des fichiers de configuration temporaires. La norme JEP 514 résout ces problèmes en proposant un processus en une seule étape, à la fois efficace et convivial, ouvrant ainsi la voie à une adoption plus large des optimisations AOT à mesure que le projet Leyden progresse.
La norme JEP 514 introduit une nouvelle option de ligne de commande, -XX:AOTCacheOutput
, dans le lanceur java
. Utilisée seule, cette option permet au lanceur d'exécuter les étapes d'apprentissage et de création de cache en une seule commande. De plus, une nouvelle variable d'environnement, JDK_AOT_VM_OPTIONS
, permet de transmettre des options de ligne de commande spécifiques à la création de cache, sans affecter l'exécution de l'apprentissage.
Avant JEP 514
# Step 1: Training run
java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf -cp app.jar com.example.App
# Step 2: Create AOT cache
java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf -XX:AOTCache=app.aot app.jar com.example.App
# Production run
java -XX:AOTCache=app.aot -cp app.jar com.example.App
Après JEP 514
# Single command to train and create AOT cache
java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App
# Production run
java -XX:AOTCache=app.aot -cp app.jar com.example.App
JEP 515 : Profilage anticipé des méthodes
La norme JEP 515 permet de stocker et d'utiliser les profils d'exécution de méthodes d'une exécution précédente (entraînement) au démarrage d'une application. L'objectif est de permettre au compilateur JIT de la JVM HotSpot de générer du code natif immédiatement au démarrage, plutôt que d'attendre la collecte des profils en début d'exécution.
Il étend le cache AOT existant (introduit dans la JEP 483) afin de stocker non seulement les classes chargées/liées au démarrage, mais aussi les profils d'exécution de méthodes collectés lors d'un entraînement. Il ne désactive ni ne remplace le profilage à l'exécution : même avec des profils mis en cache, la JVM continue de collecter les données de profilage en production, car le comportement réel peut différer de celui de l'entraînement.
JEP 518 : Échantillonnage coopératif JFR
La norme JEP 518 améliore la stabilité de JDK Flight Recorder (JFR) lors de la collecte d'échantillons asynchrones de piles de threads Java. Au lieu d'analyser les trames de pile à des points arbitraires (ce qui peut entraîner des plantages ou des traces de pile incorrectes), JFR parcourt et analyse les piles uniquement aux safepoints.
Pour réduire les problèmes causés par la restriction des parcours de pile aux points de sécurité (safepoints bias), JFR utilise une approche d'échantillonnage coopératif : il enregistre rapidement les « demandes d'échantillons » (par exemple, juste le compteur de programme + le pointeur de pile) et diffère la capture complète de la pile jusqu'à ce que le thread cible atteigne son prochain safepoint.
Conformément à la norme JEP 518, lorsqu'il est temps de prélever un échantillon de temps d'exécution JFR :
- 1. Le thread d'échantillonnage suspend le thread cible et n'enregistre que les informations légères (compteur de programme + pointeur de pile) dans une requête. Il n'essaie pas d'analyser ou de parcourir la pile immédiatement (sauf en cas de safepoint).
- 2. Il met en file d'attente cette « requête d'échantillon » (file d'attente locale du thread), puis laisse le thread cible poursuivre son exécution jusqu'à son prochain safepoint.
- 3. Au point de sécurité, le thread cible vérifie les demandes d'échantillons en attente et, pour chacune d'elles, reconstruit une trace de pile sécurisée, puis émet l'événement d'échantillonnage.
JEP 519 : En-tête d’objet compact
La norme JEP 519 fait passer les en-têtes d'objet compacts du statut expérimental au statut de produit (stable) dans le JDK 25. Cela signifie qu'ils ne nécessitent plus l'indicateur « expérimental » pour être activés.
Avant JEP 519
java -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders -jar myapp.jar
Après JEP 519
java -XX:+UseCompactObjectHeaders -jar myapp.jar
Les en-têtes d'objet compacts ont été introduits dans le JDK 24 sous la JEP 450 comme fonctionnalité expérimentale pour réduire la taille des en-têtes d'objet. Depuis, ils ont démontré une bonne stabilité et de bonnes performances : en production (services Oracle et Amazon) et lors de benchmarks, ils ont démontré des économies de mémoire, une réduction du temps processeur, une réduction des GCs, etc.
JEP 520 : JFR Method Timing & Tracing
La norme JEP 520 ajoute des fonctionnalités de chronométrage et de traçage de méthodes au JDK Flight Recorder (JFR) grâce à l'instrumentation du bytecode. Deux nouveaux événements JFR sont introduits : jdk.MethodTiming (pour l'enregistrement des compteurs et des temps d'exécution) et jdk.MethodTrace (pour le traçage des appels de méthodes individuels avec les traces de pile).
Les méthodes à chronométrer ou à tracer peuvent être sélectionnées via des filtres, la configuration, jcmd, JMX ou via des arguments de démarrage JVM.
JEP 521 : Generational Shenandoah
JEP 521 fait du mode générationnel du ramasse-miettes Shenandoah une fonctionnalité de produit dans JDK 25. Auparavant, ce mode était expérimental (introduit dans JDK 24 via JEP 404).
Dans JDK 24, pour utiliser le mode générationnel de Shenandoah, vous deviez transmettre des options de ligne de commande de la machine virtuelle, notamment :
-XX:+UseShenandoahGC
-XX:+UnlockExperimentalVMOptions
-XX:ShenandoahGCMode=generational
Dans le JDK 25, cette option étant désormais une fonctionnalité produit et -XX:+UnlockExperimentalVMOptions
n'est plus nécessaire. Vous pouvez activer le mode générationnel simplement avec :
-XX:+UseShenandoahGC
-XX:ShenandoahGCMode=generational
JEP 511 : Déclaration d’importation de modules
La JEP 511 introduit les déclarations d'importation de modules en langage Java. Une déclaration d'importation de modules a la forme import module M;
et permet d'importer toutes les classes et interfaces publiques de premier niveau de chaque paquet exporté par le module M
, y compris celles dont dépendent les modules M (transitivement) le cas échéant. Cette fonctionnalité vise à simplifier l'utilisation des API modulaires en réduisant le nombre d'instructions import
standard. Elle est finalisée dans le JDK 25, après avoir été en préversion dans les JDK précédents.
Avant JEP 511
// Without module import
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.nio.file.Path;
import java.io.IOException;
public class MyApp {
public Map<String, String> process(List<String> input, Function<String, String> fn) throws IOException {
Path p = Path.of("file.txt");
// ... etc
}
}
Vous avez besoin d'instructions d'importation distinctes pour chaque package/package à la demande ou classe spécifique.
Après JEP 511
// With module import
import module java.base;
import module java.util;
import module java.io;
public class MyApp {
public Map<String, String> process(List<String> input, Function<String, String> fn) throws IOException {
Path p = Path.of("file.txt");
// ... etc
}
}
Encore plus simplement :
import module java.base;
import module java.io;
public class MyApp {
public Map<String, String> process(List<String> input, Function<String, String> fn) throws IOException {
Path p = Path.of("file.txt");
// etc
}
}
Étant donné que java.base importe java.util, java.nio.file, l'importation de modules java.base permet d'intégrer un grand nombre de ces éléments sans nécessiter l'importation de chaque package/classe. (Remarque : selon la manière dont les modules exportent les packages et les exigences transitives, certaines importations de modules peuvent déjà intégrer les classes nécessaires, réduisant ainsi l'encombrement des importations.)
JEP 512 : Fichiers source et méthodes principales d’instance compactes
JEP 512 finalise un ensemble de fonctionnalités visant à simplifier l'écriture et la lecture des programmes Java, notamment les programmes courts ou « débutants », sans introduire de nouveau dialecte. Il permet de compacter les fichiers sources : pour les petits programmes, il n'est pas nécessaire de déclarer une classe, un module ou un package ; il suffit d'écrire directement les méthodes/champs.
Introduit également une nouvelle java.lang.IO
classe pour les I/O de console de base, afin que les débutants puissent écrire IO.println(...)
plutôt que System.out.println(...)
.
Avant JEP 512
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Après JEP 512
void main() {
IO.println("Hello, World!");
}
JEP 513 : Corps de constructeur flexibles
La norme JEP 513 autorise l'insertion d'instructions avant l'invocation explicite d'un constructeur (c'est-à -dire super(...) ou this(...)) dans le corps d'un constructeur. Ces instructions ne doivent pas référencer l'objet en construction (utilisant this), sauf pour initialiser ses propres attributs non encore initialisés.
Avant JEP 513
class Person {
int age;
Person(int age) {
if (age < 0)
throw new IllegalArgumentException("Age must be >= 0");
this.age = age;
}
}
class Employee extends Person {
String officeID;
Employee(int age, String officeID) {
// Must call super first (implicitly or explicitly)
super(age);
if (age < 18 || age > 67)
throw new IllegalArgumentException("Employee age must be between 18 and 67");
this.officeID = officeID;
}
}
Après JEP 513
class Person {
int age;
Person(int age) {
if (age < 0)
throw new IllegalArgumentException("Age must be >= 0");
this.age = age;
}
}
class Employee extends Person {
String officeID;
Employee(int age, String officeID) {
// Prologue: early validation
if (age < 18 || age > 67)
throw new IllegalArgumentException("Employee age must be between 18 and 67");
// Prologue: initialize subclass field before super
this.officeID = officeID;
super(age); // Now allowed after some checks and field assignment
}
}
API
JEP 506 : Valeurs ciblées
La JEP 506 introduit les valeurs ciblées, qui permettent le partage de données immuables au sein d'un thread, entre une méthode et ses appelants, ainsi qu'avec les threads enfants. Les valeurs ciblées sont plus faciles à comprendre et plus légères en mémoire et en temps d’exécution que ThreadLocal, notamment lorsqu'elles sont utilisées avec des threads virtuels (JEP 444) et une concurrence structurée (JEP 505).
Avant JEP 506 (en utilisant ThreadLocal)
public class Framework {
private static final ThreadLocal<FrameworkContext> CONTEXT = new ThreadLocal<>();
public void serve(Request request, Response response) {
FrameworkContext ctx = createContext(request);
CONTEXT.set(ctx);
try {
Application.handle(request, response);
} finally {
CONTEXT.remove(); // need to clean up to avoid leaks
}
}
public PersistedObject readKey(String key) {
FrameworkContext ctx = CONTEXT.get();
if (ctx == null) {
throw new IllegalStateException("No context");
}
Database db = getDatabase(ctx);
return db.readKey(key);
}
}
Après JEP 506 (using Scoped Values)
import java.lang.ScopedValue;
import static java.lang.ScopedValue.where;
public class Framework {
private static final ScopedValue<FrameworkContext> CONTEXT = ScopedValue.newInstance();
public void serve(Request request, Response response) {
FrameworkContext ctx = createContext(request);
where(CONTEXT, ctx).run(() -> {
Application.handle(request, response);
});
}
public PersistedObject readKey(String key) {
FrameworkContext ctx = CONTEXT.get(); // safe to call here if inside a run
if (ctx == null) {
throw new IllegalStateException("No context");
}
Database db = getDatabase(ctx);
return db.readKey(key);
}
}
JEP 510 : API de fonction de dérivation de clé
La JEP 510 finalise l'API de fonction de dérivation de clés du JDK 25. Précédemment présentée (dans la JEP 478) et livrée en avant-première dans le JDK 24, elle fait désormais partie des API standard du JDK 25. La JEP introduit une nouvelle classe javax.crypto.KDF
pour les algorithmes de dérivation de clés, tels que HKDF (HMAC-Extract-and-Expand) et autres KDF. Elle fournit des méthodes de dérivation SecretKey (deriveKey)
et de données brutes (deriveData
), paramétrées par des spécifications d'algorithme. Elle inclut également des classes de spécifications de paramètres pour HKDF (par exemple, extraction, expansion, extraction-puis-expansion).
Les fonctions de dérivation de clés sont largement utilisées dans la cryptographie moderne : pour générer plusieurs clés à partir d'un secret partagé, dans des protocoles tels que l'échange de clés hybrides, TLS 1.3, HPKE, etc. Disposer d'une API Java standard et bien conçue pour les KDF est essentiel pour l'exactitude, l'interopérabilité et la maintenabilité.
Avant JEP 510
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.SecretKey;
import java.security.SecureRandom;
public class HKDFExampleBefore {
public static SecretKey deriveAesKey(byte[] ikm, byte[] salt, byte[] info) throws Exception {
// Extract step
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec saltKey = new SecretKeySpec(salt, "HmacSHA256");
hmac.init(saltKey);
byte[] prk = hmac.doFinal(ikm);
// Expand step
byte[] okm = new byte[32]; // 256 bits
byte[] t = new byte[0];
int iterations = (int) Math.ceil((double)okm.length / hmac.getMacLength());
byte[] output = new byte[0];
for (int i = 1; i <= iterations; i++) {
hmac.init(new SecretKeySpec(prk, "HmacSHA256"));
hmac.update(t);
hmac.update(info);
hmac.update((byte) i);
t = hmac.doFinal();
output = concat(output, t);
}
byte[] aesKeyBytes = Arrays.copyOf(output, 32);
return new SecretKeySpec(aesKeyBytes, "AES");
}
}
Après JEP 510
import javax.crypto.KDF;
import javax.crypto.SecretKey;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.HKDFParameterSpec;
public class HKDFExampleAfter {
public static SecretKey deriveAesKey(byte[] ikm, byte[] salt, byte[] info) throws Exception {
// Instantiate the KDF with algorithm name
KDF hkdf = KDF.getInstance("HKDF-SHA256");
// Build parameter spec: do extract then expand
AlgorithmParameterSpec params = HKDFParameterSpec.ofExtract()
.addIKM(ikm)
.addSalt(salt)
.thenExpand(info, 32); // derive 32 bytes
// Derive AES key directly
SecretKey aesKey = hkdf.deriveKey("AES", params);
return aesKey;
}
}
Preview / Experimental / Incubator JEPs en Java 25
Bien que cet article se concentre sur les JEP finalisés de Java 25 (les fonctionnalités que vous pouvez utiliser en toute sécurité en production dès aujourd'hui), la version inclut également plusieurs JEP en preview, d'incubation et expérimentaux. Ceux-ci représentent des fonctionnalités encore en évolution : les previews sont presque terminées mais peuvent subir des ajustements, les incubateurs exposent les API pour un retour d'expérience précoce, et les fonctionnalités expérimentales sont exploratoires et pourraient être modifiées de manière significative ou supprimées. Voici un aperçu des prochaines fonctionnalités.
JEP 470 : Encodages PEM d’objets cryptographiques (aperçu)
Ajoute une API pour encoder et décoder des objets cryptographiques (clés publiques/privées, certificats, CRL) au format PEM largement utilisé.
JEP 502 : Valeurs stables (aperçu)
Les valeurs stables sont des objets qui contiennent des données immuables, mais contrairement aux champs final, leur initialisation peut être différée (« au plus une initialisation ») et elles sont ensuite traitées par la JVM presque comme des constantes.
JEP 505 : Accès simultané structuré (cinquième aperçu)
Fournit des API (principalement StructuredTaskScope) pour traiter un groupe de tâches (threads, y compris les threads virtuels) comme une unité structurée : les démarrer ensemble, attendre, gérer les échecs/annulations de manière propre, gérer les durées de vie sous forme de blocs/unités.
JEP 507 : Types primitifs dans les patterns, instanceof et switch (troisième aperçu)
Étend la correspondance de modèles, instanceof, et switch afin que les types primitifs (par exemple int, long, float) puissent être utilisés dans des contextes de modèles ou switch avec des modèles, pas seulement des types de référence.
JEP 508 : API vectorielle (10e incubateur)
Fournit une API d'incubation pour exprimer des calculs vectoriels (de type SIMD), permettant aux programmes Java d'écrire explicitement des opérations vectorielles correspondant à des instructions vectorielles CPU optimales à l'exécution sur les machines compatibles. Ce n'est pas encore finalisé ; il en est à sa dixième phase d'incubation.
JEP 509 : Profilage du temps de CPU JFR (expérimental)
Ajout du profilage du temps CPU à Java Flight Recorder (sous Linux, expérimental). Ainsi, au lieu d'échantillonner selon l'horloge ou le temps écoulé, vous obtenez des échantillons basés sur le temps CPU consommé, même en incluant le code natif. Nouveau type d'événement jdk.CPUTimeSample
.
Conclusion
Java 25 réaffirme l'évolution constante de la plateforme, alliant fonctionnalités matures et prêtes pour la production à des innovations d'avenir. Les JEP finalisées apportent des améliorations tangibles en termes de performances, de sécurité et de productivité des développeurs, tandis que les fonctionnalités en preview, d'incubation et expérimentales laissent entrevoir l'avenir du langage et de la JVM. Ensemble, ils témoignent d'une volonté claire de maintenir Java moderne, expressif et performant. Comme toujours, adopter dès aujourd'hui les fonctionnalités stables tout en gardant un œil sur les nouvelles fonctionnalités vous permettra de bénéficier du meilleur des deux mondes : fiabilité et innovation.
❓ FAQ – Tout ce que vous devez savoir sur la version Java 25
Q : Qu’est-ce que Java 25 apporte de nouveau par rapport à Java 21 ?
R : Java 25 introduit plusieurs fonctionnalités finalisées comme l’import de modules simplifié (JEP 511), la compaction des fichiers sources (JEP 512), des constructeurs flexibles (JEP 513), ainsi que des améliorations côté JVM (JEP 518–521) et des API enrichies. Il inclut aussi des fonctionnalités en preview et expérimentales pour préparer l’avenir.
Q : Est-ce que les nouvelles fonctionnalités sont compatibles avec les versions précédentes de Java ?
R : Oui, les JEP finalisées sont rétrocompatibles et prêtes pour la production. Les JEP en preview ou expérimentales sont optionnelles et peuvent nécessiter des ajustements pour fonctionner avec les anciens codes.
Q : Les JEP en preview ou incubateur sont-elles sûres à utiliser ?
R : Elles sont destinées à l’expérimentation. Vous pouvez les tester pour vous familiariser avec les futures fonctionnalités, mais elles peuvent changer ou être supprimées dans les versions suivantes.
Q : Pourquoi l’import de modules (JEP 511) est-il utile ?
R : Il simplifie les instructions import
en réduisant le nombre de lignes nécessaires pour inclure des modules complets, ce qui rend le code plus lisible et plus maintenable.
Q : Faut-il mettre à jour immédiatement son environnement vers Java 25 ?
R : Pour les projets en production, il est recommandé de vérifier d’abord la compatibilité avec vos dépendances. Les nouvelles fonctionnalités peuvent être adoptées progressivement, en commençant par les JEP finalisées.
Q : Quels gains côté JVM avec Java 25 ?
R : La JVM bénéficie d’optimisations comme le profilage anticipé des méthodes (JEP 515), le sampling coopératif JFR (JEP 518) et la génération de Shenandoah optimisée (JEP 521), ce qui améliore les performances et la stabilité des applications.
Q : Où trouver les ressources officielles pour Java 25 et ses JEP ?
R : Toutes les JEP sont documentées sur openjdk.org, et les builds de Java 25 peuvent être téléchargés sur jdk.java.net/25.