SlideShare une entreprise Scribd logo
Les nouveautés
de Java 21
@jmdoudoux
JMdoudouX
Luxembourg, 14 mai 2024
Sciam 2
Java 21 est diffusé le 19 septembre 2023
JDK 21 est une version LTS
Depuis la réduction de 3 à 2 ans entre 2 LTS après Java 17
OpenJDK 21 est l’implémentation de référence
Des spécifications de la version 21 de Java SE
Des patchs seront disponibles
Au moins jusqu’à la prochaine version LTS
Java SE en 2024
Java poursuit son évolution
En respectant le rythme de releases tous les 6 mois
Mis en place depuis 7 ans
Sciam 3
Java 21
Les spécifications dans la JSR 396
15 JEPs :
Les nouvelles fonctionnalités
En Preview
JEP 430 : String Templates (Preview)
JEP 443 : Unnamed Patterns and Variables (Preview)
JEP 445 : Unnamed Classes and Instance Main Methods (Preview)
Standard
JEP 431 : Sequenced Collections
JEP 439 : Generational ZGC
JEP 449 : Deprecate the Windows 32-bit x86 Port for Removal
JEP 451 : Prepare to Disallow the Dynamic Loading of Agents
JEP 452 : Key Encapsulation Mechanism API
JEP 440 : Record Patterns
JEP 441 : Pattern Matching for switch
Les fonctionnalités qui sortent de preview
JEP 444 : Virtual Threads
Les fonctionnalités qui restent en preview ou incubation
JEP 448 : Vector API (Sixth Incubator)
JEP 453 : Structured Concurrency (Preview)
JEP 442 : Foreign Function & Memory API (Third Preview)
JEP 446 : Scoped Values (Preview)
Sciam 4
Directeur Technique
chez
Auteur d’un didacticiel depuis 2001
Diffusé sous licence GNU FDL
• Développons en Java (4200 pages)
Co-fondateur du
Jean-Michel Doudoux
@jmdoudoux
https://www.jmdoudoux.fr
Sciam 5
Roadmap
Les fonctionnalités
du projet Amber
du projet Loom
Les évolutions
Les fonctionnalités du projet Panama
dans les API Java Core
dans la JVM Hotspot
dans les outils du JDK
dans la sécurité
Les fonctionnalités
du projet Amber
Record Patterns
Pattern Matching for switch
String Templates (Preview)
Unnamed Patterns and Variables (Preview)
Unnamed Classes and Instance Main Methods (Preview)
Sciam 7
Record Patterns
En preview en Java 19 (JEP 420) et 20 (JEP 432)
Standard en Java 21
Type pattern utilisable avec le pattern matching sur les records
Ajouter un nouveau pattern utilisable dans le pattern matching
Le record pattern pour déconstruire les valeurs d’un record
record Employe(String nom, String prenom) {}
Object o = new Employe("Nom1", "Prenom1");
if (o instanceof Employe emp) {
System.out.println("Employe : "+emp.nom()+" "+emp.prenom());
}
if (o instanceof Employe(String nom, String prenom)) {
System.out.println("Employe : "+nom+" "+prenom);
}
Sciam 8
Record Patterns
Utilisable avec une instruction instanceof ou switch
Type pattern et record pattern peuvent être combinés
Dans un même switch
Java 20 : utilisable dans une boucle for améliorée
switch (o) {
case Employe emp -> System.out.println(emp);
case Grade(String code,String designation) -> System.out.println("Grade " + designation + "(" + code + ")");
default -> System.out.println("Type non supporté");
}
List<Employe> employes = List.of(new Employe("Nom1", "Prenom1"));
for(Employe(var nom, var prenom) : employes) {
System.out.println(nom + " " + prenom);
}
Java 21 : retiré
Sciam 9
Record Patterns
Le nom des variables peut être différents de celui des composants
Seuls l’ordre et le type des composants doivent être respectés
Utilisation possible de l’inférence du type dans le pattern
Java 20 : support de l'inférence des types d’arguments génériques
record Mono<T>(T val) implements Container<T> {}
Mono<Mono<String>> monoDeMono = new Mono<>(new Mono<>("valeur"));
if (o instanceof Employe(var nom, var prenom)) {
System.out.println("Employe : "+nom+" "+prenom);
}
if (o instanceof Employe(String n, String p)) {
System.out.println("Employe : " + n + " " + p);
}
if (monoDeMono instanceof Mono(Mono(var s))) {
System.out.println("mono contient " + s);
}
Sciam 10
Record Patterns
Les record patterns peuvent être imbriqués
La valeur null ne correspond à aucun record pattern
record Grade(String code, String designation) {}
record Employe(String nom, String prenom, Grade grade) {}
Object o = new Employe("Nom1", "Prenom1", new Grade("DEV", "Développeur"));
if (o instanceof Employe(var nom, var prenom, Grade(var code, var designation))) {
System.out.println("Employe : " + nom + " " + prenom + ", "+ designation);
}
Sciam 11
Pattern Matching for switch
Historiquement 4 preview en Java
17 (JEP 406), 18 (JEP 420), 19 (JEP 427) et 20 (JEP 433)
But : utiliser le pattern matching dans une instruction switch
En maintenant la compatibilité syntaxique
Avec un support de la valeur null contrairement à la levée historique d’une NPE
Standard en Java 21
static String getDesignation(Object obj) {
String designation = switch (obj) {
case Terrain t -> "Terrain";
case null -> "Instance null";
default -> "Pas un terrain";
};
return designation;
}
Plusieurs patterns utilisables
Type pattern
Teste la correspondance sur un type
Sciam 12
Pattern Matching for switch
Record Pattern
Déconstruit un record
Utilisation possible d’un Guarded pattern case label
Combine un pattern et une expression booléenne dans un case
Parenthesized pattern retiré en Java 21
static String getDesignation(Object obj) {
String designation = switch (obj) {
case Terrain t
when (t.getSurface() > 1000) -> "Grand terrain";
case Terrain t -> "Petit terrain";
case null -> "Instance null";
default -> "Pas un terrain";
};
return designation;
}
Utilise le nouveau mot clé contextuel when
Sciam 13
Pattern Matching for switch
Possibilité de mixer constantes et patterns
Au runtime une exception de type java.lang.MatchException est levée
Exemple : sur un type scellé ou une énumération par exemple
L’exhaustivité des cas d’un switch doit être satisfaite
Sinon erreur de compilation : implique fréquemment l’utilisation d’un default
String getEnv(String env) {
return switch (env) {
case "Prod" -> "Production";
case String s -> "Hors production";
};
}
static String formater(Number nombre) {
return switch (nombre) { // erreur : the switch expression does not cover all possible input values
case Integer i -> String.format("Entier %d", i);
case Long l -> String.format("Entier long %d", l);
}; }
Sciam 14
Pattern Matching for switch
Le compilateur vérifie la dominance des patterns
Les patterns les plus restrictifs doivent être avant les moins restrictifs
Idem avec les guarded patterns case label
static String formater(Object o) {
return switch (o) {
case Number n -> String.format("Nombre %f", n);
case Integer i -> String.format("Entier %d", i); // erreur this case label is dominated by a preceding case label
case Long l -> String.format("Entier long %d", l);
default -> o.toString();
};
}
String taille = switch (chaine) {
case String s -> "Moyenne";
case String s when s.length() < 10 -> "Petite"; // erreur this case label is dominated by a preceding case label
case String s when s.length() > 100 -> "Grande";
};
Sciam 15
Pattern Matching for switch
Sémantique d'exécution d’un switch avec le pattern matching
Lorsque la valeur est null et que le cas null n’est pas explicitement géré :
Elle est étroitement alignée sur la sémantique historique des switchs
public static void main(String[] args) {
String chaine = null;
switch (chaine) {
case String s -> {
System.out.println("traitement chaine");
System.out.println("taille : " + s.length());
}
}
}
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:233)
at TestSwitchPattern.main(TestSwitchPattern.java:5)
Sciam 16
Pattern Matching for switch
Java 21 autorise les constantes d'énumération qualifiées
Dans les cases des switchs afin d’éviter d’avoir à utiliser un guarded pattern case label
public sealed interface MonInterface permits MonEnum, MaClasse {}
public enum MonEnum implements MonInterface { PAIRE, IMPAIRE }
public final class MaClasse implements MonInterface {}
// …
static void traiter(MonInterface c) {
switch (c) {
// case MonEnum e when e == MonEnum.PAIRE -> { System.out.println("Paire"); }
case MonEnum.PAIRE -> { System.out.println("Paire"); }
case MonEnum.IMPAIRE -> { System.out.println("Impaire"); }
case MonEnum e -> { System.out.println("MonEnum"); }
case MaClasse mc -> { System.out.println("MaClasse"); }
}
}
Sciam 17
String Templates (Preview)
Courant de devoir créer des chaînes de caractères composées
À partir d'une combinaison de textes littéraux
Et de valeurs ou d'expressions
De nombreux langages proposent l'interpolation de chaînes
Comme alternative à la concaténation de chaînes
Historiquement plusieurs fonctionnalités, toutes avec inconvénients
String s = x + " + " + y + " = " + (x + y);
String s = String.format("%2$d + %1$d = %3$d", x, y, x + y);
String t = "%2$d + %1$d = %3$d".formatted(x, y, x + y);
MessageFormat mf = new MessageFormat("{0} + {1} = {2}");
String s = mf.format(x, y, x + y);
String s = new StringBuilder()
.append(x)
.append(" + ")
.append(y)
.append(" = ")
.append(x + y)
.toString();
Sciam 18
String Templates (Preview)
Mais le résultat peut parfois engendrer des soucis indirects
Exemple : SQL ou JSON injection
En combinant :
- Un texte littéral avec des expressions intégrées
- Et un processeur de templates
Le but : enrichir le langage Java avec des string templates
Qui complètent les chaînes littérales et les blocs de texte
Pour produire des chaînes de caractères construites dynamiquement
Avec la clarté de l’interpolation et un résultat plus sûr
Possibilité de créer une instance de type quelconque
Avec un processeur de templates personnalisés
Sciam 19
String Templates (Preview)
Nouveau type d'expression dans le langage : les templates expressions
Pour effectuer une interpolation de chaîne pour créer une chaîne ou un objet
Une template expression est composée de trois éléments :
1) Un processeur de templates (STR)
2) Un caractère point (U+002E), celui utilisé dans les autres expressions
3) Un template ("Bonjour {prenom}")
qui contient une expression intégrée ({prenom})
Syntaxiquement, ressemble à une chaîne littérale avec un préfixe :
String prenom = "Jean-Michel";
String message = STR."Bonjour {prenom}";
Le template peut utiliser plusieurs lignes de code source
En utilisant une syntaxe similaire à celle des blocs de texte
String prenom = "Jean-Michel";
String message = STR."""
Bonjour
{prenom}""";
Sciam 20
String Templates (Preview)
3 processeurs de templates fournis dans le JDK
FormatProcessor.FMT : effectue une interpolation pour créer une chaîne
Il interprète les spécificateurs de format à gauche des expressions intégrées
Les spécificateurs de format sont ceux définis dans java.util.Formatter
StringTemplace.STR : effectue une interpolation pour créer une chaîne
int x = 10, y = 20;
String s = STR."{x} + {y} = {x + y}";
StringTemplate.RAW : produit un objet de type StringTemplate
import static java.util.StringTemplate.RAW;
…
String prenom = "Jean-Michel";
StringTemplate st = RAW."Bonjour {prenom}";
String message = STR.process(st);
import static java.util.FormatProcessor.FMT;
…
int x = 10, y = 20;
String message = FMT."%05d{a} + %05d{b} = %05d{a + b}";
Sciam 21
String Templates (Preview)
Possibilité de définir des processeurs de templates personnalisés
Pour générer des chaînes ou des objets qui peuvent être validés
Implémenter l’unique méthode process()
Une instance de l'interface fonctionnelle StringTemplate.Processor
var JSON = StringTemplate.Processor.of((StringTemplate st) -> new JSONObject(st.interpolate()));
String nom = "Durant";
String prenom = "Pierre";
JSONObject doc = JSON."""
{
"nom": "{nom}",
"prenom": "{prenom}"
}""";
Utilisation de la fabrique StringTemplate.Processor::of
Pour obtenir une instance
Sciam 22
Unnamed Patterns and Variables (Preview)
Enrichit le langage d’une syntaxe
Pour les patterns inutilisés dans les records pattern imbriqués
Et les variables inutilisées qui doivent être déclarées
Unnamed pattern : un pattern inconditionnel qui ne correspond à rien
Utilisable dans un pattern imbriqué à la place d'un type ou record pattern
Utilisant le 51eme mot clé réservé de Java : _
record Grade(String code, String designation) {}
record Employe(String nom, String prenom, Grade grade) {}
Object o = new Employe("Nom1", "Prenom1", new Grade("DEV", "Développeur"));
if (o instanceof Employe(var nom, _, _)) {
System.out.println("Employe : " + nom);
}
Sciam 23
Unnamed Patterns and Variables (Preview)
Unnamed pattern variable :
Utilisable avec tous types de patterns
Unnamed variable : peut être initialisée mais non utilisée dans
Une variable locale dans un bloc
Une ressource dans un try-with-resources
L'en-tête d'une boucle for et for améliorée
Une exception d'un bloc catch
Un paramètre formel d'une expression Lambda
if (o instanceof Personne(var nom, var _, var _)) {
System.out.println("Personne : " + nom);
}
try (var _ = ScopedContext.acquire()) {
var _ = service.traiter((_, _) -> System.out.printn("traiter"));
} catch (Throwable _) { }
Utilisable plusieurs fois dans la même portée
Sciam 24
Unnamed Patterns and Variables (Preview)
Utile dans des switchs avec des patterns sur des types scellés
Risque de bug
en cas d’ajout d’un type
Impossible d’avoir plusieurs patterns
nommés dans un case
sealed interface Forme permits Cercle, Carre, Rectangle {}
void traiterFormeRonde(Forme forme) {
switch(forme) {
case Cercle c -> afficher(c);
case Carre c -> {}
case Rectangle r -> {}
}
}
switch(forme) {
case Cercle c -> afficher(c);
default -> {}
}
switch(forme) {
case Cercle c -> afficher(c);
case Carre _, Rectangle _ -> {}
}
Utilisation possible de default
Préférable d’utiliser
des unnamed variables
Sciam 25
Unnamed Classes and Instance Main Methods (Preview)
Plutôt compliqué
Les buts :
Faire évoluer le langage pour simplifier les programmes simples
Et faciliter l’apprentissage des débutants avec le langage Java
Deux évolutions dans une classe unique
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
void main() {
System.out.println("Hello world");
}
Classe sans nom (unnamed class)
class HelloWorld {
void main() {
System.out.println("Hello world");
}
}
Méthode d’instance main()
Les fonctionnalités
du projet Loom
Virtual Threads
Structured Concurrency (Preview)
Scoped Values (Preview)
Sciam 27
Virtual Threads
Chaque thread Java est directement mappé à un thread de l’OS
Depuis Java 1.0
Ce modèle n’est pas optimal
Car un thread de la plateforme est coûteux en ressources
Notamment à cause de la taille fixe (par défaut) de sa pile
Décharge la JVM de se préoccuper de tâches réalisées par l’OS
Ordonnancement et changement de contexte des threads
Cela limite le nombre de threads qui peuvent être utilisés
Or on utilise de plus en plus de threads
Qui généralement passent beaucoup de temps à attendre la fin d’une opération bloquante
Sciam 28
Virtual Threads
Introduction d’un nouveau type de threads : des threads virtuels
En standard Java 21 (JEP 444)
En preview Java 19 (JEP 425) et 20 (JEP 436)
Ce sont des threads « légers » gérés dans la JVM
Non lié à un thread de la plate-forme dédié
Qu’ils utilisent uniquement lors de l’utilisation de la CPU par ses traitements
Plusieurs objectifs :
• Conserver le style « un thread par requête »
Avec une meilleure utilisation des ressources requises
• Assurer une adoption par le code existant
Qui utilise l'API java.lang.Thread avec un impact minimum
• Permettre le débogage, le profilage et le dépannage
Avec les outils existants du JDK
Sciam 29
Virtual Threads
Exécutent du code qui ne bloque pas les threads de l’OS
Contrairement aux verrous ou aux opérations bloquantes (I/O par exemple)
Si le thread virtuel exécute une action bloquante dans les API du JDK
La JVM enregistrement la stack dans le heap et exécute l’action en non bloquante
Mapping M:N entre threads virtuels et threads de l’OS
Grâce à un ForkJoinPool dédié qui fournit les threads porteurs (carrier threads)
Le thread porteur peut alors exécuter un autre thread virtuel
Une fois l’action non bloquante terminée
L’exécution des traitements du thread virtuel est reprise sur un thread porteur
Potentiellement différent
Ce mécanisme est géré en interne par les API du JDK
En utilisant des objets de type Continuation et ContinuationScope
Et est transparent pour le développeur
Sciam 30
Virtual Threads
La classe finale package-private java.lang.VirtualThread
Hérite de java.lang.Thread
Pas de constructeur public
L’interface scellée Thread.Buidler propose deux interfaces filles
Thread.Builder.OfVirtual et Thread.Builder.OfPlatform
Dont on obtient une instance avec Thread::ofVirtual et Thread::ofPlatform
Le plus simple est d’utiliser Thread::startVirtualThread
Pour démarrer un nouveau thread virtuel qui exécute un Runnable
Thread t = Thread.startVirtualThread(() -> {
System.out.println("Thread : " + Thread.currentThread());
});
var threadVirtuel = Thread.ofVirtual().name("app-thread-virtuel-", 0).start(() -> {
System.out.println(Thread.currentThread());
});
threadVirtuel.join();
Sciam 31
Virtual Threads
Plusieurs restrictions sur les threads virtuels :
Ne pas mettre les threads virtuels dans un pool
Aucune utilité vue leur faible coût de création
• Ils sont obligatoirement des threads démons
• La priorité est obligatoirement Thread.NORM_PRIORITY
• stop(), resume(), suspend() lèvent une UnsupportedOperationException
• Ils ne peuvent pas être associés à un ThreadGroup
• getThreadGroup() renvoie un groupe "VirtualThreads" fictif qui est vide
• getAllStackTraces() renvoie une Map qui contient uniquement que les threads de l’OS
plutôt que de tous les threads
Sciam 32
Virtual Threads
Peuvent améliorer le débit des applications
Lorsque le nombre de tâches simultanées est important
Et que les tâches ne requièrent pas de manière intensive la CPU
• L’exécution d’un bloc de code synchronized
Il est préférable d’utiliser si possible un ReentrantLock
Deux scénarios bloquants peuvent limiter l’intérêt des threads virtuels
Car ils laissent le thread virtuel associé à son thread porteur
-Djdk.tracePinnedThreads=full ou short
• Lors de l’exécution d’une méthode native
Option de la JVM pour identifier ces cas
Un événement JFR : jdk.VirtualThreadPinned
Sciam 33
Structured Concurrency (preview)
Propose un nouveau modèle de programmation
Grâce au traitement de plusieurs tâches
Exécutées dans différents threads virtuels comme une seule unité de travail
Le but : simplifier la programmation multithread
En rationalisant la gestion des erreurs et l'annulation,
En améliorant la fiabilité et en renforçant l'observabilité
Fork/Join et concurrence structurée sont complémentaires
Fork/Join
Conçu pour traiter des tâches à forte
intensité de calcul sur une courte durée
Utilise des threads de l’OS
Complexe à mettre œuvre
Concurrence structurée
Conçue pour traiter des tâches à forte
intensité d'E/S
Utilise des threads virtuels
Facile à mettre en œuvre
Sciam 34
Structured Concurrency (preview)
API en incubation en Java 20
Le modèle permet une écriture du code dans un style synchrone
Avec une exécution en asynchrone
Le code est ainsi facile à écrire, à lire et à tester
API en preview en Java 21
Dans le package java.util.concurrent
Classe principale : java.util.concurrent.StructuredTaskScope
Sciam 35
 Créer une instance dans un try-with-resource
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException,
TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope()) { 
}
return resultat;
}
Structured Concurrency (preview)
 Invoquer la méthode fork() pour chaque sous-tâche à exécuter
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException,
TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope()) { 
Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient)); 
Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande));
}
return resultat;
}
 Attendre la fin de l’exécution des sous-tâches
• Soit sans timeout en utilisant la méthode join()
• Soit avec timeout en utilisant la méthode joinUntil()
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException,
TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope()) { 
Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient)); 
Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande));
scope.joinUntil(Instant.now().plusSeconds(15)); 
}
return resultat;
}
La mise en œuvre en plusieurs étapes
Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException,
TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope()) { 
Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient)); 
Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande));
scope.joinUntil(Instant.now().plusSeconds(15)); 
resultat = this.genererFacture(clientFuture.get(), commandeFuture.get()); 
}
return resultat;
}
 Exploiter les résultats obtenus dans des instances de type Subtask (Future en Java 20)
Sciam 36
Structured Concurrency (preview)
La classe StructuredTaskScope.ShutdownOnFailure
Propose un modèle invoke all
Qui exécute toutes les sous-tâches
Et termine toutes les sous-tâches en cours si une sous-tâche lève une exception
Facture getFacture(String codeClient, long idCommande)
throws ExecutionException, InterruptedException, TimeoutException {
Facture resultat = null;
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient));
Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande));
scope.joinUntil(Instant.now().plusSeconds(15));
scope.throwIfFailed();
resultat = this.genererFacture(clientFuture.get(), commandeFuture.get());
}
return resultat;
}
Sciam 37
Structured Concurrency (preview)
La classe StructuredTaskScope.ShutdownOnSuccess
Propose un modèle invoke any
Qui renvoie le résultat de la première sous-tâche terminée
Et termine les autres sous-tâches restantes
Temperature getTemperature(String ville) throws InterruptedException, ExecutionException {
Temperature resultat = null;
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<Temperature>()) {
serviceMeteos.forEach(f -> {
scope.fork(() -> f.getTemperature(ville));
}
);
scope.join();
resultat = scope.result();
}
return resultat;
}
Sciam 38
Structured Concurrency (preview)
Possibilité de créer son propre scope
En héritant de la classe StructuredTaskScope
Et en y implémentant ses propres règles métiers
class ComposantLePlusLegerScope extends StructuredTaskScope<Composant> {
private final Collection<Composant> composants = new ConcurrentLinkedQueue<>();
private final Collection<Throwable> exceptions = new ConcurrentLinkedQueue<>();
@Override
protected void handleComplete(Subtask<? extends Composant> subtask) {
switch (subtask.state()) {
case SUCCESS -> this.composants.add(subtask.get());
case FAILED -> this.exceptions.add(subtask.exception());
case UNAVAILABLE -> {}
}
}
Sciam 39
Structured Concurrency (preview)
public Exception exceptions() {
RuntimeException exception = new RuntimeException("Impossible d'obtenir le composant le plus leger");
exceptions.forEach(exception::addSuppressed);
return exception;
}
public Composant getComposant() throws Exception {
return composants.stream().min(Comparator.comparing(Composant::poids))
.orElseThrow(this::exceptions);
}
}
Sciam 40
Scoped Values (Preview)
Pour partager des objets dans le code exécuté par un thread
Historiquement depuis Java 1.2, on utilise une variable de type ThreadLocal
L’API ScopedValue tente de remédier à ces inconvénients
Mais cela présente plusieurs risques :
Mutable, fuite de mémoire, consommation de ressources
public final static ScopedValue<String> VALEUR = ScopedValue.newInstance();
En incubation dans Java 20 (JEP 429)
En preview dans Java 21 (JEP 446)
Création d’une instance généralement statique et publique
Stocker et de partager des données immuables
Pour une durée de vie limitée à des traitements du thread qui les a écrits
Sciam 41
Scoped Values (Preview)
where() pour définir une valeur, chainable pour plusieurs valeurs
run() : sous la forme d’une implémentation de Runnable
Pour exécuter une tâche dans le thread courant
String valeur = ScopedValue.where(VALEUR, "test")
.<String>call(monService::traiter);
System.out.println((VALEUR.isBound() ? VALEUR.get() : "non definie"));
ScopedValue.where(VALEUR, "test").run(() -> { afficherValeur(); });
get() pour obtenir la valeur ou lève une NoSuchElementException
isBound() pour savoir si une valeur est associée au thread
Ou call() : sous la forme d’une implémentation de Callable
Sciam 42
Scoped Values (Preview)
Réassociation d’une valeur pour un traitement sous-jacent
Partage avec les threads virtuels d’une StucturedTaskScope
ScopedValue.where(VALEUR, "test", () -> {
try (var scope = new StructuredTaskScope<String>()) {
afficherValeur();
scope.fork(monServiceA::traiter);
scope.fork(monServiceB::traiter);
scope.joinUntil(Instant.now().plusSeconds(10));
} catch (InterruptedException | TimeoutException e) {
e.printStackTrace();
}
});
ScopedValue.where(VALEUR, "test").run(() -> {
afficherValeur(); // test
ScopedValue.where(VALEUR, "test-autre").run(monService::traiter); // test-autre
afficherValeur(); // test
});
Les évolutions
dans les API de Java Core
Sequenced Collections
Evolutions diverses
Sciam 44
Sequenced collections
L’API Collections propose des collections ordonnées
Mais n’est pas homogène dans les fonctionnalités proposées
Chacune avec leurs méthodes, dont certaines pas évidentes ou inexistantes
Les opérations liées à l'ordre de parcours sont soit incohérentes soit absentes
Des implémentations permettent d'obtenir le premier ou le dernier élément
Idem pour le parcours dans l’ordre inverse
Premier élément Dernier élément
List list.get(0) list.get(list.size() - 1)
Deque deque.getFirst() deque.getLast()
SortedSet sortedSet.first() sortedSet.last()
LinkedHashSet linkedHashSet.iterator().next()
Introduire 3 nouvelles interfaces
Pour représenter des collections avec un ordre de parcours défini
SequencedCollection, SequencedSet et SequencedMap
Sciam 45
Sequenced collections
Elles possèdent :
Des éléments parcourables du premier au dernier élément dans un certain ordre
Fournissent des API uniformes pour accéder au 1er et dernier élément,
Ajouter / supprimer le premier / dernier élément
Et pour parcourir ses éléments dans l'ordre inverse
Sciam 46
La classe java.net.http.HttpClient
Implémente l’interface AutoClosable
Plusieurs méthodes ont été ajoutées pour gérer l’arrêt :
boolean awaitTermination(Duration)
Bloque jusqu'à ce que toutes les opérations soient terminées
ou jusqu'à ce que la durée soit écoulée
void close()
boolean isTerminated()
void shutdown()
Demande un arrêt ordonné des requêtes précédemment soumises
Aucune nouvelle requête n'est acceptée
void shutdownNow()
try (var client = HttpClient.newHttpClient()) {
// utilisation du client
}
Sciam 47
Les évolutions dans les autres API
Ajout dans les classes java.lang.StringBuffer et StringBuilder
repeat(CharSequence, int) et repeat(int, int)
Ajout dans la classe java.lang.String
int indexOf(String str, int beginIndex, int endIndex)
int indexOf(int ch, int beginIndex, int endIndex)
String[] splitWithDelimiters(String regex, int limit)
Agit comme la méthode split() mais renvoie aussi le délimiteur
Ajout dans la classe java.lang.Math et StrictMath
Surcharges de clamp(valeur, min, max)
la valeur retournée est comprise entre min et max
Pour les types primitifs double, float, long et int
Ajout dans la classe java.util.regex.Pattern
String[] splitWithDelimiters(CharSequence input int limit)
Agit comme la méthode split() mais renvoie aussi le délimiteur
Sciam 48
Les évolutions dans les autres API
Ajout de méthodes dans la classe java.utils.Collections
newSequencedSetFromMap(SequencedMap)
unmodifiableSequencedCollection(SequencedCollection)
unmodifiableSequencedMap(SequencedMap)
unmodifiableSequencedSet(SequencedSet)
void shuffle(List<?>, RandomGenerator) : surcharge avec RandomGenerator
Ajout de méthodes dans la classe java.util.Locale
Stream<Locale> availableLocales()
String caseFoldLanguageTag(String)
pour formatter le code langue selon la RFC5646
String langue = Locale.caseFoldLanguageTag("fr-fr"); // fr-FR
var randomizer = RandomGenerator.getDefault();
Collections.shuffle(liste,randomizer);
Ajout de méthodes dans la classe java.lang.Character
pour le support des Emojis
Les évolutions
dans la JVM HotSpot
Generational ZGC
Deprecate the Windows 32-bit x86 Port for Removal
Prepare to Disallow the Dynamic Loading of Agents
Sciam 50
Generational ZGC
Rendre le ramasse-miettes ZGC générationnel
Activation avec les options : -XX:+UseZGC -XX:+ZGenerational
Tout en maintenant les caractéristiques actuelles
ZGC générationnel devrait être une meilleure solution
Pour la plupart des cas d'utilisation que le ZGC non générationnel
Les tailles de tas allant de quelques centaines de Mo à 16 To
Les temps de pause ne doivent pas dépasser 1 milliseconde
Sciam 51
Les évolutions dans G1
Durant les Full GC, autorisation des objets volumineux à être déplacés
Permet de réduire les risques d’OOME liés à la fragmentation des régions
Le « Hot Card Cache » a été retiré
Permet de réduire la consommation de mémoire native de 0.2%
Durant les full GC, amélioration du compactage
La fonction de GC préventifs a été complètement retirée
Le tear down et set up des TLABs par thread ont été parallélisés
Pour réduire les temps de pauses avec beaucoup de threads
Sciam 52
Prepare to Disallow the Dynamic Loading of Agents
Le but est d’émettre un avertissement
Lorsqu’un agent est chargé dynamiquement dans une JVM en cours d'exécution
Utilisation l'option -XX:+EnableDynamicAgentLoading de la JVM
Pour permettre de charger dynamiquement des agents sans avertissement
Pour préparer les utilisateurs à une future version
Qui interdira le chargement dynamique des agents par défaut
Sauf pour les outils de maintenance
L’option -Djdk.instrument.traceUsage de la JVM
Affiche un message et une stacktrace
Lors de l’invocation de l’API java.lang.Instrument
Facilite l’identification les bibliothèques
Qui utilisent des agents chargés dynamiquement
Les évolutions
dans les outils du JDK
Sciam 54
Les vues JFR (views)
Support des vues JFR
Qui permettent d’afficher une agrégation d’événements
La commande JFR.view de la commande jcmd sur une JVM
Par défaut les 10 dernières minutes ou les derniers 32Mo
Modifiable avec les options les options maxage et maxsize
Sur un enregistrement : l’option view de la commande jfr
Plusieurs options de formatage
+/- 70 vues prédéfinies sont proposées
hot-methods, gc-pauses, pinned-threads, gc, memory-leaks-by-class, ...
Pour obtenir la liste des vues : jcmd <pid> JFR.view ou jfr view
jfr view [--verbose] [--width <integer>] [--truncate <mode>] [--cell-height <integer>] <view> <file>
jcmd <pid > JFR.view <view>
Sciam 55
Erreur si plusieurs ; entre imports
Avant Java 21
Le compilateur javac autorise d’avoir plusieurs ; entre des imports
Java 21
Le compilateur javac interdit d’avoir plusieurs points-virgules entre imports
C:>javac MaClasse.java
MaClasse.java:1: error: extraneous semicolon
import java.util.List;;
^
1 error
import java.util.List;;
import java.util.Set;
class MaClasse { }
C:>javac MaClasse.java
C:>
Les évolutions
dans la sécurité
L’API Key Encapsulation Mecanism
Sciam 57
La sécurité
Mises à jour de certificats racine des CA dans le keystore cacert
Support de l’algorithme de signature standard "HSS/LMS" (RFC 8554)
Augmentation de la taille par défaut des clés de plusieurs algorithmes
L'implémentation du KeychainStore de macOS
Expose maintenant les certificats avec une confiance appropriée
Dans le domaine de l'utilisateur, le domaine de l'administrateur, ou les deux
Les options -genseckey et –importpass de keytool affichent un warning
Lorsque l'option –keyalg utilise des algo basés sur des mots de passe faibles
Suppression de l’API ContentSigner
Et des options -altsigner et –altsignerpath de l’outil jarsigner
Sciam 58
L’API Key Encapsulation Mecanism
KEM permet l’échange d’une clé symétrique partagée sécurisée
En utilisant les API du package javax.crypto
KeyPairGenerator kpg = KeyPairGenerator.getInstance("X25519");
KeyPair kp = kpg.generateKeyPair();
Un générateur d’une paire de clé (publique/privée)
KEM kemSender = KEM.getInstance("DHKEM");
KEM.Encapsulator sender = kemSender.newEncapsulator(kp.getPublic());
KEM.Encapsulated encapsulated = sender.encapsulate();
// envoyer le byte[] retourné par encapsulated.encapsulation()
KEM kemReceiver = KEM.getInstance("DHKEM");
KEM.Decapsulator receiver = kemReceiver.newDecapsulator(kp.getPrivate());
SecretKey sharedKey = receiver.decapsulate(encapsulated.encapsulation());
Une fonction d’encapsulation qui utilise la clé publique pour chiffrer
Une fonction de désencapsulation qui utilise la clé privée pour déchiffrer
Configuration possible des algorithmes de génération de clé et de chiffrement
Les fonctionnalités
du projet Panama
Foreign Function & Memory API (Third Preview)
Vector API (Sixth Incubator)
Sciam 60
Foreign Function & Memory API (Third Preview)
Historiquement, fusion de 2 JEPs introduites en incubation :
Foreign-Memory Access API en Java 14 (JEP 370, 383, et 393)
Et Foreign Linker API en Java 16 (JEP 389)
Proposée en incubation en Java 17 (JEP 412) et Java 18 (JEP 419)
Attention : cette API évolue beaucoup
Dans chacune des versions de Java où elle est proposée
Proposée en preview en Java 19 (JEP 424), 20 (JEP 434) et 21 (JEP 442)
Elle est maintenant dans le package java.lang.foreign du module java.base
API de bas niveau pour de manière simple, sûre et efficace :
• Accéder à des données en mémoire hors du tas (off heap memory)
Alternative à certaines fonctionnalités
De java.nio.ByteBuffer (pas performante mais sûre)
Et sun.misc.Unsafe (non standard)
• Invoquer des fonctions natives
Une future alternative à l’API JNI présente depuis Java 1.1
Sciam 61
Foreign Function & Memory API (Third Preview)
Requière une bonne connaissance des mécanismes natifs
De bas niveau utilisés (adresses, segments, formats des données,…)
Définit des classes et des interfaces permettant :
D’appeler des fonctions étrangères :
SymbolLookup, FunctionDescriptor, Linker
De contrôler l'allocation et la désallocation de la mémoire étrangère :
MemorySegment, SegmentAllocator, Arena
De manipuler et accéder à la mémoire étrangère structurée :
MemoryLayout et dérivées
En utilisant des fonctionnalités d’Invoke Dynamique
VarHandle pour l’accès accès aux données
MethodHandle pour l’invocation de méthodes
Autoriser les appels natifs --enable-native-access=ALL-UNNAMED
Pour éviter un warning et dans le futur une erreur pour des raisons de sécurité
Sciam 62
Foreign Function & Memory API (Third Preview)
Contrôle de l'allocation et la désallocation de la mémoire étrangère
L’interface Arena qui hérite de SegmentAllocator
Gère l’accès à la mémoire native et leur isolation dans une portée
Et s’assure que les segments alloués seront libérés et qu’il n’y a pas d’accès après libération
4 Arena proposées :
global arena : une seule, partagée par tous les threads,
Libération des segments à l’arrêt de la JVM
Arena arena = Arena.global();
automatic arena : gérée par le ramasse-miettes,
Utilisable par plusieurs threads,
Libération des segments lorsqu’il n’y a plus de référence
Arena arena = Arena.ofAuto();
confined arena : les segments sont libérés à la fermeture
De l’arena, utilisable que par le thread qui l’a créé
try (Arena arena = Arena.ofConfined()) { . . . }
shared arena : combine un confined arena
Avec une utilisation multi-threads
Arena arena = Arena.ofShared();
Sciam 63
Foreign Function & Memory API (Third Preview)
MemorySegment décrit une zone de mémoire off heap
Allocation avec surcharges de méthodes de SegmentAllocator
Arena::allocate(long)
Alloue un segment dont la taille en octets est précisée
Notamment :
Arena::allocateFrom(String)
Helper qui alloue un segment pour une chaîne en UTF-8 au format C
Arena::allocate(MemoryLayout)
Pour allouer un segment correspondant à une structure
MemorySegment chaineNative = arena.allocateFrom("Java");
MemorySegment segment = arena.allocate(100);
Sciam 64
Foreign Function & Memory API (Third Preview)
Manipulation et accès à la mémoire étrangère structurée
MemoryLayout définit la structure de mémoire d'un type spécifique
ValueLayout hérite de MemoryLayout
Définit la façon dont les types de base sont stockés en mémoire
Exemple :
ValueLayout.JAVA_LONG décrit le type Java primitif long
ValueLayout.ADDRESS décrit une adresse mémoire du matériel sous-jacent
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(10 * 4); // allouer pour 10 entiers de type int (4 octets)
for (int i = 0 ; i < 10 ; i++) {
segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
}
}
Sciam 65
Foreign Function & Memory API (Third Preview)
SequenceLayout hérite de MemoryLayout
Décrit un tableau d'un type spécifique décrit par un MemoryLayout
StructLayout hérite de MemoryLayout
Décrit une structure de types qui se suivent en mémoire
// tableau de 10 doubles Java
MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_DOUBLE);
// un tableau composé de trois tableaux de dix tableaux entiers
MemoryLayout.sequenceLayout(3, MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT));
StructLayout dateStruct = MemoryLayout.structLayout(
ValueLayout.JAVA_SHORT.withName("annee"),
ValueLayout.JAVA_SHORT.withName("mois"),
ValueLayout.JAVA_SHORT.withName("jour"));
Sciam 66
Foreign Function & Memory API (Third Preview)
Manipulation de bas niveau d’un segment
Manipulation d’un segment structuré avec des VarHandles
MemorySegment segment = arena.allocate((long)(2 * 4 * 10), 1); // 10 * 2 * 4 octets (int)
for (int i = 0; i < 10; i++) {
segment.setAtIndex(ValueLayout.JAVA_INT, (i * 2), i); // x
segment.setAtIndex(ValueLayout.JAVA_INT, (i * 2) + 1, i); // y
}
SequenceLayout pointsLayout = MemoryLayout.sequenceLayout(10,
MemoryLayout.structLayout(ValueLayout.JAVA_INT.withName("x"), ValueLayout.JAVA_INT.withName("y")));
VarHandle xHandle = pointsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("x"));
VarHandle yHandle = pointsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("y"));
MemorySegment segment = arena.allocate(pointsLayout);
for (int i = 0; i < pointsLayout.elementCount(); i++) {
xHandle.set(segment, (long) i, i);
yHandle.set(segment, (long) i, i);
}
Sciam 67
Foreign Function & Memory API (Third Preview)
Recherche et description de fonctions étrangères
Chargement optionnel de la bibliothèque native par le ClassLoader
Avec System::loadLibrary
L’interface FunctionDescriptor
Décrit les paramètres entrants et sortants d'une fonction native
L’interface SymbolLookup
Pour localiser et rechercher l’adresse de fonctions dans les bibliothèques natives
Des fabriques pour obtenir une instance :
static SymbolLookup loaderLookup()
pour une instance sur les bibliothèques chargées par le ClassLoader
static SymbolLookup libraryLookup(String, Arena) et libraryLookup(Path, Arena)
pour une instance sur une bibliothèque qui sera chargée au besoin
Lors de l'appel de fonction native avec un MethodHandle
s'assure que les types Java transférés sont convertis en types C corrects
et que la valeur de retour est convertie d'un type C vers le type de retour Java
Sciam 68
Foreign Function & Memory API (Third Preview)
Obtenir un éditeur de liens spécifique à l’Application Binary Interface
Une ABI est un ensemble de conventions d’appel et de types de données
Associés au compilateur, au système d’exploitation et au processeur utilisés
La méthode defaultLookup() renvoie un SymbolLookup
Pour rechercher une fonction dans un ensemble de bibliothèques courantes
Exemple : libc sous Linux
L’interface Linker
Crée un éditeur de liens spécifique à l’ABI de la plate-forme native sous-jacente
Optimisé pour les conventions d'appel de nombreuses plateformes :
Linux/x64 Linux/AArch64 Linux/RISC-V Linux/PPC64 Linux/s390
macOS/x64 macOS/AArch64 Windows/x64 Windows/AArch64 AIX/ppc64
Prend en charge les conventions d'appel des autres plateformes en déléguant à libffi
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
Sciam 69
Foreign Function & Memory API (Third Preview)
Appel à des fonctions étrangères via des MethodHandles
Linker permet une interaction aller-retour
Entre le code Java et les fonctions étrangères
Les appels descendants (downcall)
Appels du code Java vers le code natif
Les appels ascendants (upcall)
Appels du code natif vers le code Java
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").get(), FunctionDescriptor.of(JAVA_LONG, ADDRESS)
);
try (Arena arena = Arena.ofConfined()) {
MemorySegment chaine = arena.allocateFrom("Java");
long len = (long) strlen.invoke(chaine);
}
Sciam 70
Foreign Function & Memory API (Third Preview)
Exemple Java 21 sous Windows
public class DialogFFM {
public static void main(String[] args) {
try {
System.loadLibrary("user32");
Optional<MemorySegment> msgBoxFunction = SymbolLookup.loaderLookup().find("MessageBoxA");
FunctionDescriptor msgBoxFunctionDesc = FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS, ADDRESS, JAVA_INT);
Linker linker = Linker.nativeLinker();
MethodHandle methodHandle = linker.downcallHandle(msgBoxFunction.get(), msgBoxFunctionDesc);
try (Arena offHeap = Arena.ofConfined()) {
MemorySegment cStringMessage = offHeap.allocateUtf8String("Voulez-vous utiliser Java 21 ?");
MemorySegment cStringTitre = offHeap.allocateUtf8String("Confirmation");
int bouton = (int) methodHandle.invoke(NULL, cStringMessage, cStringTitre, 36);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
Sciam 71
Vector API (Sixth Incubator)
Exprimer des calculs vectoriels
Qui, au moment de l'exécution, sont systématiquement compilés
Avec les meilleures instructions vectorielles possibles sur l’architectures CPU
Les SIMD sur les CPU supportées : x64 (SSE et AVX) et AArch64 (Neon)
Single Instruction, Multiple Data
Traiter en parallèle un tableau de données, sans threads
Pour appliquer une même opération sur plusieurs valeurs traitées
En un seul cycle de traitement CPU
En incubation en Java 16 (JEP 338), 17 (JEP 414), 18 (JEP 417),
19 (JEP 426), 20 (JEP 438) et 21 (JEP 448)
Dans le module jdk.incubator.vector
Sciam 72
Vector API (Sixth Incubator)
API plutôt de bas niveau, verbeuse, dépendant partiellement du CPU
Mais qui peut offrir de meilleures performances que le code scalaire équivalent
Exemple Java 21
static float[] calculerScalaire(float[] a, float[] b) {
float[] c = new float[a.length];
for (int i = 0 ; i < a.length ; i++) {
c[i] = a[i] * a[i] - b[i] * b[i];
}
return c;
}
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
static float[] calculerVectoriel(float[] a, float[] b) {
float[] c = new float[a.length];
int i = 0;
for (; i < SPECIES.loopBound(a.length) ; i += SPECIES.length()) {
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vr = va.mul(va).sub(vb.mul(vb));
vr.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = a[i] * a[i] - b[i] * b[i];
}
return c;
}
Conclusion
Sciam 74
Conclusion
Java poursuit son évolution en respectant son modèle de releases
La syntaxe, particulièrement le pattern matching
Java 21 proposent des fonctionnalités concernant
N’hésitez pas à télécharger un JDK 21
Cela permettra à Java de rester pertinent aujourd’hui et demain
La programmation parallèle et concurrente
Une meilleure utilisation du matériel moderne
Pour utiliser cette version LTS du JDK
Merci
pour votre attention
Questions ?

Contenu connexe

Similaire à Les nouveautés de Java 21 - YaJUG 2024.pdf

Présentation de ECMAScript 6
Présentation de ECMAScript 6Présentation de ECMAScript 6
Présentation de ECMAScript 6
Julien CROUZET
 

Similaire à Les nouveautés de Java 21 - YaJUG 2024.pdf (20)

Devoxx France 2023 - Les nouveautés de Java 19 et 20
Devoxx France 2023 - Les nouveautés de Java 19 et 20Devoxx France 2023 - Les nouveautés de Java 19 et 20
Devoxx France 2023 - Les nouveautés de Java 19 et 20
 
Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5Améliorations dans Java depuis la version 5
Améliorations dans Java depuis la version 5
 
Développement informatique : Chaines de caractères et expressions regulières
Développement informatique : Chaines de caractères et expressions regulièresDéveloppement informatique : Chaines de caractères et expressions regulières
Développement informatique : Chaines de caractères et expressions regulières
 
Chapitre 2: String en Java
Chapitre 2:  String en JavaChapitre 2:  String en Java
Chapitre 2: String en Java
 
JAVA
JAVAJAVA
JAVA
 
Présentation de ECMAScript 6
Présentation de ECMAScript 6Présentation de ECMAScript 6
Présentation de ECMAScript 6
 
Java 5, un blian
Java 5, un blianJava 5, un blian
Java 5, un blian
 
Java 5, un bilan
Java 5,  un bilanJava 5,  un bilan
Java 5, un bilan
 
Change mind about JS
Change mind about JSChange mind about JS
Change mind about JS
 
.php1 : les fondamentaux du PHP
.php1 : les fondamentaux du PHP.php1 : les fondamentaux du PHP
.php1 : les fondamentaux du PHP
 
COURS_PYTHON_22.ppt
COURS_PYTHON_22.pptCOURS_PYTHON_22.ppt
COURS_PYTHON_22.ppt
 
JAVA Chapitre7
JAVA Chapitre7JAVA Chapitre7
JAVA Chapitre7
 
Introduction à JavaScript
Introduction à JavaScriptIntroduction à JavaScript
Introduction à JavaScript
 
Librairies Java qui changent la vie
Librairies Java qui changent la vieLibrairies Java qui changent la vie
Librairies Java qui changent la vie
 
Introduction java
Introduction javaIntroduction java
Introduction java
 
Introduction à Python - Achraf Kacimi El Hassani
Introduction à Python - Achraf Kacimi El HassaniIntroduction à Python - Achraf Kacimi El Hassani
Introduction à Python - Achraf Kacimi El Hassani
 
Introduction à JavaScript
Introduction à JavaScriptIntroduction à JavaScript
Introduction à JavaScript
 
Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)
 
C# 7 - Nouveautés
C# 7 - NouveautésC# 7 - Nouveautés
C# 7 - Nouveautés
 
Javascript Json artchitecture
Javascript  Json artchitecture Javascript  Json artchitecture
Javascript Json artchitecture
 

Plus de Jean-Michel Doudoux

Plus de Jean-Michel Doudoux (13)

Javaday Paris 2022 - Java en 2022 : profiter de Java 17
Javaday Paris 2022 - Java en 2022 : profiter de Java 17Javaday Paris 2022 - Java en 2022 : profiter de Java 17
Javaday Paris 2022 - Java en 2022 : profiter de Java 17
 
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDK
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDKVoxxeddays Lux 2022 - Profiling et monitoring avec le JDK
Voxxeddays Lux 2022 - Profiling et monitoring avec le JDK
 
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdfdevoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
devoxx 2022 - 10 ans de Devoxx FR et de Java.pdf
 
Lyon JUG 2018 - Java le changement c'est maintenant
Lyon JUG 2018 - Java le changement c'est maintenantLyon JUG 2018 - Java le changement c'est maintenant
Lyon JUG 2018 - Java le changement c'est maintenant
 
Nantes jug 2018 - Java le changement c'est maintenant
Nantes jug 2018 - Java le changement c'est maintenantNantes jug 2018 - Java le changement c'est maintenant
Nantes jug 2018 - Java le changement c'est maintenant
 
Voxxeddays lux 2018 apres java 8, java 9 et 10
Voxxeddays lux 2018 apres java 8, java 9 et 10Voxxeddays lux 2018 apres java 8, java 9 et 10
Voxxeddays lux 2018 apres java 8, java 9 et 10
 
Anniversaire Paris JUG - Deja 10 ans - retour vers le futur avec JMX
Anniversaire Paris JUG -  Deja 10 ans - retour vers le futur avec JMXAnniversaire Paris JUG -  Deja 10 ans - retour vers le futur avec JMX
Anniversaire Paris JUG - Deja 10 ans - retour vers le futur avec JMX
 
Devoxx 2018 Après Java 8, Java 9 et 10
Devoxx 2018 Après Java 8, Java 9 et 10Devoxx 2018 Après Java 8, Java 9 et 10
Devoxx 2018 Après Java 8, Java 9 et 10
 
Apres java 8, java 9 et 10 - BreizhCamp 2018
Apres java 8, java 9 et 10 - BreizhCamp 2018Apres java 8, java 9 et 10 - BreizhCamp 2018
Apres java 8, java 9 et 10 - BreizhCamp 2018
 
Java 9 modulo les modules devoxx fr 2017
Java 9 modulo les modules devoxx fr 2017Java 9 modulo les modules devoxx fr 2017
Java 9 modulo les modules devoxx fr 2017
 
Voxxdays luxembourg 2016 retours java 8
Voxxdays luxembourg 2016 retours java 8Voxxdays luxembourg 2016 retours java 8
Voxxdays luxembourg 2016 retours java 8
 
Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016
 
"Input/Ouput, 16 ans après" à Devoxx France 2012
"Input/Ouput, 16 ans après" à Devoxx France 2012"Input/Ouput, 16 ans après" à Devoxx France 2012
"Input/Ouput, 16 ans après" à Devoxx France 2012
 

Les nouveautés de Java 21 - YaJUG 2024.pdf

  • 1. Les nouveautés de Java 21 @jmdoudoux JMdoudouX Luxembourg, 14 mai 2024
  • 2. Sciam 2 Java 21 est diffusé le 19 septembre 2023 JDK 21 est une version LTS Depuis la réduction de 3 à 2 ans entre 2 LTS après Java 17 OpenJDK 21 est l’implémentation de référence Des spécifications de la version 21 de Java SE Des patchs seront disponibles Au moins jusqu’à la prochaine version LTS Java SE en 2024 Java poursuit son évolution En respectant le rythme de releases tous les 6 mois Mis en place depuis 7 ans
  • 3. Sciam 3 Java 21 Les spécifications dans la JSR 396 15 JEPs : Les nouvelles fonctionnalités En Preview JEP 430 : String Templates (Preview) JEP 443 : Unnamed Patterns and Variables (Preview) JEP 445 : Unnamed Classes and Instance Main Methods (Preview) Standard JEP 431 : Sequenced Collections JEP 439 : Generational ZGC JEP 449 : Deprecate the Windows 32-bit x86 Port for Removal JEP 451 : Prepare to Disallow the Dynamic Loading of Agents JEP 452 : Key Encapsulation Mechanism API JEP 440 : Record Patterns JEP 441 : Pattern Matching for switch Les fonctionnalités qui sortent de preview JEP 444 : Virtual Threads Les fonctionnalités qui restent en preview ou incubation JEP 448 : Vector API (Sixth Incubator) JEP 453 : Structured Concurrency (Preview) JEP 442 : Foreign Function & Memory API (Third Preview) JEP 446 : Scoped Values (Preview)
  • 4. Sciam 4 Directeur Technique chez Auteur d’un didacticiel depuis 2001 Diffusé sous licence GNU FDL • Développons en Java (4200 pages) Co-fondateur du Jean-Michel Doudoux @jmdoudoux https://www.jmdoudoux.fr
  • 5. Sciam 5 Roadmap Les fonctionnalités du projet Amber du projet Loom Les évolutions Les fonctionnalités du projet Panama dans les API Java Core dans la JVM Hotspot dans les outils du JDK dans la sécurité
  • 6. Les fonctionnalités du projet Amber Record Patterns Pattern Matching for switch String Templates (Preview) Unnamed Patterns and Variables (Preview) Unnamed Classes and Instance Main Methods (Preview)
  • 7. Sciam 7 Record Patterns En preview en Java 19 (JEP 420) et 20 (JEP 432) Standard en Java 21 Type pattern utilisable avec le pattern matching sur les records Ajouter un nouveau pattern utilisable dans le pattern matching Le record pattern pour déconstruire les valeurs d’un record record Employe(String nom, String prenom) {} Object o = new Employe("Nom1", "Prenom1"); if (o instanceof Employe emp) { System.out.println("Employe : "+emp.nom()+" "+emp.prenom()); } if (o instanceof Employe(String nom, String prenom)) { System.out.println("Employe : "+nom+" "+prenom); }
  • 8. Sciam 8 Record Patterns Utilisable avec une instruction instanceof ou switch Type pattern et record pattern peuvent être combinés Dans un même switch Java 20 : utilisable dans une boucle for améliorée switch (o) { case Employe emp -> System.out.println(emp); case Grade(String code,String designation) -> System.out.println("Grade " + designation + "(" + code + ")"); default -> System.out.println("Type non supporté"); } List<Employe> employes = List.of(new Employe("Nom1", "Prenom1")); for(Employe(var nom, var prenom) : employes) { System.out.println(nom + " " + prenom); } Java 21 : retiré
  • 9. Sciam 9 Record Patterns Le nom des variables peut être différents de celui des composants Seuls l’ordre et le type des composants doivent être respectés Utilisation possible de l’inférence du type dans le pattern Java 20 : support de l'inférence des types d’arguments génériques record Mono<T>(T val) implements Container<T> {} Mono<Mono<String>> monoDeMono = new Mono<>(new Mono<>("valeur")); if (o instanceof Employe(var nom, var prenom)) { System.out.println("Employe : "+nom+" "+prenom); } if (o instanceof Employe(String n, String p)) { System.out.println("Employe : " + n + " " + p); } if (monoDeMono instanceof Mono(Mono(var s))) { System.out.println("mono contient " + s); }
  • 10. Sciam 10 Record Patterns Les record patterns peuvent être imbriqués La valeur null ne correspond à aucun record pattern record Grade(String code, String designation) {} record Employe(String nom, String prenom, Grade grade) {} Object o = new Employe("Nom1", "Prenom1", new Grade("DEV", "Développeur")); if (o instanceof Employe(var nom, var prenom, Grade(var code, var designation))) { System.out.println("Employe : " + nom + " " + prenom + ", "+ designation); }
  • 11. Sciam 11 Pattern Matching for switch Historiquement 4 preview en Java 17 (JEP 406), 18 (JEP 420), 19 (JEP 427) et 20 (JEP 433) But : utiliser le pattern matching dans une instruction switch En maintenant la compatibilité syntaxique Avec un support de la valeur null contrairement à la levée historique d’une NPE Standard en Java 21 static String getDesignation(Object obj) { String designation = switch (obj) { case Terrain t -> "Terrain"; case null -> "Instance null"; default -> "Pas un terrain"; }; return designation; } Plusieurs patterns utilisables Type pattern Teste la correspondance sur un type
  • 12. Sciam 12 Pattern Matching for switch Record Pattern Déconstruit un record Utilisation possible d’un Guarded pattern case label Combine un pattern et une expression booléenne dans un case Parenthesized pattern retiré en Java 21 static String getDesignation(Object obj) { String designation = switch (obj) { case Terrain t when (t.getSurface() > 1000) -> "Grand terrain"; case Terrain t -> "Petit terrain"; case null -> "Instance null"; default -> "Pas un terrain"; }; return designation; } Utilise le nouveau mot clé contextuel when
  • 13. Sciam 13 Pattern Matching for switch Possibilité de mixer constantes et patterns Au runtime une exception de type java.lang.MatchException est levée Exemple : sur un type scellé ou une énumération par exemple L’exhaustivité des cas d’un switch doit être satisfaite Sinon erreur de compilation : implique fréquemment l’utilisation d’un default String getEnv(String env) { return switch (env) { case "Prod" -> "Production"; case String s -> "Hors production"; }; } static String formater(Number nombre) { return switch (nombre) { // erreur : the switch expression does not cover all possible input values case Integer i -> String.format("Entier %d", i); case Long l -> String.format("Entier long %d", l); }; }
  • 14. Sciam 14 Pattern Matching for switch Le compilateur vérifie la dominance des patterns Les patterns les plus restrictifs doivent être avant les moins restrictifs Idem avec les guarded patterns case label static String formater(Object o) { return switch (o) { case Number n -> String.format("Nombre %f", n); case Integer i -> String.format("Entier %d", i); // erreur this case label is dominated by a preceding case label case Long l -> String.format("Entier long %d", l); default -> o.toString(); }; } String taille = switch (chaine) { case String s -> "Moyenne"; case String s when s.length() < 10 -> "Petite"; // erreur this case label is dominated by a preceding case label case String s when s.length() > 100 -> "Grande"; };
  • 15. Sciam 15 Pattern Matching for switch Sémantique d'exécution d’un switch avec le pattern matching Lorsque la valeur est null et que le cas null n’est pas explicitement géré : Elle est étroitement alignée sur la sémantique historique des switchs public static void main(String[] args) { String chaine = null; switch (chaine) { case String s -> { System.out.println("traitement chaine"); System.out.println("taille : " + s.length()); } } } Exception in thread "main" java.lang.NullPointerException at java.base/java.util.Objects.requireNonNull(Objects.java:233) at TestSwitchPattern.main(TestSwitchPattern.java:5)
  • 16. Sciam 16 Pattern Matching for switch Java 21 autorise les constantes d'énumération qualifiées Dans les cases des switchs afin d’éviter d’avoir à utiliser un guarded pattern case label public sealed interface MonInterface permits MonEnum, MaClasse {} public enum MonEnum implements MonInterface { PAIRE, IMPAIRE } public final class MaClasse implements MonInterface {} // … static void traiter(MonInterface c) { switch (c) { // case MonEnum e when e == MonEnum.PAIRE -> { System.out.println("Paire"); } case MonEnum.PAIRE -> { System.out.println("Paire"); } case MonEnum.IMPAIRE -> { System.out.println("Impaire"); } case MonEnum e -> { System.out.println("MonEnum"); } case MaClasse mc -> { System.out.println("MaClasse"); } } }
  • 17. Sciam 17 String Templates (Preview) Courant de devoir créer des chaînes de caractères composées À partir d'une combinaison de textes littéraux Et de valeurs ou d'expressions De nombreux langages proposent l'interpolation de chaînes Comme alternative à la concaténation de chaînes Historiquement plusieurs fonctionnalités, toutes avec inconvénients String s = x + " + " + y + " = " + (x + y); String s = String.format("%2$d + %1$d = %3$d", x, y, x + y); String t = "%2$d + %1$d = %3$d".formatted(x, y, x + y); MessageFormat mf = new MessageFormat("{0} + {1} = {2}"); String s = mf.format(x, y, x + y); String s = new StringBuilder() .append(x) .append(" + ") .append(y) .append(" = ") .append(x + y) .toString();
  • 18. Sciam 18 String Templates (Preview) Mais le résultat peut parfois engendrer des soucis indirects Exemple : SQL ou JSON injection En combinant : - Un texte littéral avec des expressions intégrées - Et un processeur de templates Le but : enrichir le langage Java avec des string templates Qui complètent les chaînes littérales et les blocs de texte Pour produire des chaînes de caractères construites dynamiquement Avec la clarté de l’interpolation et un résultat plus sûr Possibilité de créer une instance de type quelconque Avec un processeur de templates personnalisés
  • 19. Sciam 19 String Templates (Preview) Nouveau type d'expression dans le langage : les templates expressions Pour effectuer une interpolation de chaîne pour créer une chaîne ou un objet Une template expression est composée de trois éléments : 1) Un processeur de templates (STR) 2) Un caractère point (U+002E), celui utilisé dans les autres expressions 3) Un template ("Bonjour {prenom}") qui contient une expression intégrée ({prenom}) Syntaxiquement, ressemble à une chaîne littérale avec un préfixe : String prenom = "Jean-Michel"; String message = STR."Bonjour {prenom}"; Le template peut utiliser plusieurs lignes de code source En utilisant une syntaxe similaire à celle des blocs de texte String prenom = "Jean-Michel"; String message = STR.""" Bonjour {prenom}""";
  • 20. Sciam 20 String Templates (Preview) 3 processeurs de templates fournis dans le JDK FormatProcessor.FMT : effectue une interpolation pour créer une chaîne Il interprète les spécificateurs de format à gauche des expressions intégrées Les spécificateurs de format sont ceux définis dans java.util.Formatter StringTemplace.STR : effectue une interpolation pour créer une chaîne int x = 10, y = 20; String s = STR."{x} + {y} = {x + y}"; StringTemplate.RAW : produit un objet de type StringTemplate import static java.util.StringTemplate.RAW; … String prenom = "Jean-Michel"; StringTemplate st = RAW."Bonjour {prenom}"; String message = STR.process(st); import static java.util.FormatProcessor.FMT; … int x = 10, y = 20; String message = FMT."%05d{a} + %05d{b} = %05d{a + b}";
  • 21. Sciam 21 String Templates (Preview) Possibilité de définir des processeurs de templates personnalisés Pour générer des chaînes ou des objets qui peuvent être validés Implémenter l’unique méthode process() Une instance de l'interface fonctionnelle StringTemplate.Processor var JSON = StringTemplate.Processor.of((StringTemplate st) -> new JSONObject(st.interpolate())); String nom = "Durant"; String prenom = "Pierre"; JSONObject doc = JSON.""" { "nom": "{nom}", "prenom": "{prenom}" }"""; Utilisation de la fabrique StringTemplate.Processor::of Pour obtenir une instance
  • 22. Sciam 22 Unnamed Patterns and Variables (Preview) Enrichit le langage d’une syntaxe Pour les patterns inutilisés dans les records pattern imbriqués Et les variables inutilisées qui doivent être déclarées Unnamed pattern : un pattern inconditionnel qui ne correspond à rien Utilisable dans un pattern imbriqué à la place d'un type ou record pattern Utilisant le 51eme mot clé réservé de Java : _ record Grade(String code, String designation) {} record Employe(String nom, String prenom, Grade grade) {} Object o = new Employe("Nom1", "Prenom1", new Grade("DEV", "Développeur")); if (o instanceof Employe(var nom, _, _)) { System.out.println("Employe : " + nom); }
  • 23. Sciam 23 Unnamed Patterns and Variables (Preview) Unnamed pattern variable : Utilisable avec tous types de patterns Unnamed variable : peut être initialisée mais non utilisée dans Une variable locale dans un bloc Une ressource dans un try-with-resources L'en-tête d'une boucle for et for améliorée Une exception d'un bloc catch Un paramètre formel d'une expression Lambda if (o instanceof Personne(var nom, var _, var _)) { System.out.println("Personne : " + nom); } try (var _ = ScopedContext.acquire()) { var _ = service.traiter((_, _) -> System.out.printn("traiter")); } catch (Throwable _) { } Utilisable plusieurs fois dans la même portée
  • 24. Sciam 24 Unnamed Patterns and Variables (Preview) Utile dans des switchs avec des patterns sur des types scellés Risque de bug en cas d’ajout d’un type Impossible d’avoir plusieurs patterns nommés dans un case sealed interface Forme permits Cercle, Carre, Rectangle {} void traiterFormeRonde(Forme forme) { switch(forme) { case Cercle c -> afficher(c); case Carre c -> {} case Rectangle r -> {} } } switch(forme) { case Cercle c -> afficher(c); default -> {} } switch(forme) { case Cercle c -> afficher(c); case Carre _, Rectangle _ -> {} } Utilisation possible de default Préférable d’utiliser des unnamed variables
  • 25. Sciam 25 Unnamed Classes and Instance Main Methods (Preview) Plutôt compliqué Les buts : Faire évoluer le langage pour simplifier les programmes simples Et faciliter l’apprentissage des débutants avec le langage Java Deux évolutions dans une classe unique public class HelloWorld { public static void main(String[] args) { System.out.println("Hello world"); } } void main() { System.out.println("Hello world"); } Classe sans nom (unnamed class) class HelloWorld { void main() { System.out.println("Hello world"); } } Méthode d’instance main()
  • 26. Les fonctionnalités du projet Loom Virtual Threads Structured Concurrency (Preview) Scoped Values (Preview)
  • 27. Sciam 27 Virtual Threads Chaque thread Java est directement mappé à un thread de l’OS Depuis Java 1.0 Ce modèle n’est pas optimal Car un thread de la plateforme est coûteux en ressources Notamment à cause de la taille fixe (par défaut) de sa pile Décharge la JVM de se préoccuper de tâches réalisées par l’OS Ordonnancement et changement de contexte des threads Cela limite le nombre de threads qui peuvent être utilisés Or on utilise de plus en plus de threads Qui généralement passent beaucoup de temps à attendre la fin d’une opération bloquante
  • 28. Sciam 28 Virtual Threads Introduction d’un nouveau type de threads : des threads virtuels En standard Java 21 (JEP 444) En preview Java 19 (JEP 425) et 20 (JEP 436) Ce sont des threads « légers » gérés dans la JVM Non lié à un thread de la plate-forme dédié Qu’ils utilisent uniquement lors de l’utilisation de la CPU par ses traitements Plusieurs objectifs : • Conserver le style « un thread par requête » Avec une meilleure utilisation des ressources requises • Assurer une adoption par le code existant Qui utilise l'API java.lang.Thread avec un impact minimum • Permettre le débogage, le profilage et le dépannage Avec les outils existants du JDK
  • 29. Sciam 29 Virtual Threads Exécutent du code qui ne bloque pas les threads de l’OS Contrairement aux verrous ou aux opérations bloquantes (I/O par exemple) Si le thread virtuel exécute une action bloquante dans les API du JDK La JVM enregistrement la stack dans le heap et exécute l’action en non bloquante Mapping M:N entre threads virtuels et threads de l’OS Grâce à un ForkJoinPool dédié qui fournit les threads porteurs (carrier threads) Le thread porteur peut alors exécuter un autre thread virtuel Une fois l’action non bloquante terminée L’exécution des traitements du thread virtuel est reprise sur un thread porteur Potentiellement différent Ce mécanisme est géré en interne par les API du JDK En utilisant des objets de type Continuation et ContinuationScope Et est transparent pour le développeur
  • 30. Sciam 30 Virtual Threads La classe finale package-private java.lang.VirtualThread Hérite de java.lang.Thread Pas de constructeur public L’interface scellée Thread.Buidler propose deux interfaces filles Thread.Builder.OfVirtual et Thread.Builder.OfPlatform Dont on obtient une instance avec Thread::ofVirtual et Thread::ofPlatform Le plus simple est d’utiliser Thread::startVirtualThread Pour démarrer un nouveau thread virtuel qui exécute un Runnable Thread t = Thread.startVirtualThread(() -> { System.out.println("Thread : " + Thread.currentThread()); }); var threadVirtuel = Thread.ofVirtual().name("app-thread-virtuel-", 0).start(() -> { System.out.println(Thread.currentThread()); }); threadVirtuel.join();
  • 31. Sciam 31 Virtual Threads Plusieurs restrictions sur les threads virtuels : Ne pas mettre les threads virtuels dans un pool Aucune utilité vue leur faible coût de création • Ils sont obligatoirement des threads démons • La priorité est obligatoirement Thread.NORM_PRIORITY • stop(), resume(), suspend() lèvent une UnsupportedOperationException • Ils ne peuvent pas être associés à un ThreadGroup • getThreadGroup() renvoie un groupe "VirtualThreads" fictif qui est vide • getAllStackTraces() renvoie une Map qui contient uniquement que les threads de l’OS plutôt que de tous les threads
  • 32. Sciam 32 Virtual Threads Peuvent améliorer le débit des applications Lorsque le nombre de tâches simultanées est important Et que les tâches ne requièrent pas de manière intensive la CPU • L’exécution d’un bloc de code synchronized Il est préférable d’utiliser si possible un ReentrantLock Deux scénarios bloquants peuvent limiter l’intérêt des threads virtuels Car ils laissent le thread virtuel associé à son thread porteur -Djdk.tracePinnedThreads=full ou short • Lors de l’exécution d’une méthode native Option de la JVM pour identifier ces cas Un événement JFR : jdk.VirtualThreadPinned
  • 33. Sciam 33 Structured Concurrency (preview) Propose un nouveau modèle de programmation Grâce au traitement de plusieurs tâches Exécutées dans différents threads virtuels comme une seule unité de travail Le but : simplifier la programmation multithread En rationalisant la gestion des erreurs et l'annulation, En améliorant la fiabilité et en renforçant l'observabilité Fork/Join et concurrence structurée sont complémentaires Fork/Join Conçu pour traiter des tâches à forte intensité de calcul sur une courte durée Utilise des threads de l’OS Complexe à mettre œuvre Concurrence structurée Conçue pour traiter des tâches à forte intensité d'E/S Utilise des threads virtuels Facile à mettre en œuvre
  • 34. Sciam 34 Structured Concurrency (preview) API en incubation en Java 20 Le modèle permet une écriture du code dans un style synchrone Avec une exécution en asynchrone Le code est ainsi facile à écrire, à lire et à tester API en preview en Java 21 Dans le package java.util.concurrent Classe principale : java.util.concurrent.StructuredTaskScope
  • 35. Sciam 35  Créer une instance dans un try-with-resource Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope()) {  } return resultat; } Structured Concurrency (preview)  Invoquer la méthode fork() pour chaque sous-tâche à exécuter Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope()) {  Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient));  Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande)); } return resultat; }  Attendre la fin de l’exécution des sous-tâches • Soit sans timeout en utilisant la méthode join() • Soit avec timeout en utilisant la méthode joinUntil() Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope()) {  Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient));  Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande)); scope.joinUntil(Instant.now().plusSeconds(15));  } return resultat; } La mise en œuvre en plusieurs étapes Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope()) {  Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient));  Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande)); scope.joinUntil(Instant.now().plusSeconds(15));  resultat = this.genererFacture(clientFuture.get(), commandeFuture.get());  } return resultat; }  Exploiter les résultats obtenus dans des instances de type Subtask (Future en Java 20)
  • 36. Sciam 36 Structured Concurrency (preview) La classe StructuredTaskScope.ShutdownOnFailure Propose un modèle invoke all Qui exécute toutes les sous-tâches Et termine toutes les sous-tâches en cours si une sous-tâche lève une exception Facture getFacture(String codeClient, long idCommande) throws ExecutionException, InterruptedException, TimeoutException { Facture resultat = null; try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Subtask<Client> clientFuture = scope.fork(() -> this.getClient(codeClient)); Subtask<Commande> commandeFuture = scope.fork(() -> this.getCommande(idCommande)); scope.joinUntil(Instant.now().plusSeconds(15)); scope.throwIfFailed(); resultat = this.genererFacture(clientFuture.get(), commandeFuture.get()); } return resultat; }
  • 37. Sciam 37 Structured Concurrency (preview) La classe StructuredTaskScope.ShutdownOnSuccess Propose un modèle invoke any Qui renvoie le résultat de la première sous-tâche terminée Et termine les autres sous-tâches restantes Temperature getTemperature(String ville) throws InterruptedException, ExecutionException { Temperature resultat = null; try (var scope = new StructuredTaskScope.ShutdownOnSuccess<Temperature>()) { serviceMeteos.forEach(f -> { scope.fork(() -> f.getTemperature(ville)); } ); scope.join(); resultat = scope.result(); } return resultat; }
  • 38. Sciam 38 Structured Concurrency (preview) Possibilité de créer son propre scope En héritant de la classe StructuredTaskScope Et en y implémentant ses propres règles métiers class ComposantLePlusLegerScope extends StructuredTaskScope<Composant> { private final Collection<Composant> composants = new ConcurrentLinkedQueue<>(); private final Collection<Throwable> exceptions = new ConcurrentLinkedQueue<>(); @Override protected void handleComplete(Subtask<? extends Composant> subtask) { switch (subtask.state()) { case SUCCESS -> this.composants.add(subtask.get()); case FAILED -> this.exceptions.add(subtask.exception()); case UNAVAILABLE -> {} } }
  • 39. Sciam 39 Structured Concurrency (preview) public Exception exceptions() { RuntimeException exception = new RuntimeException("Impossible d'obtenir le composant le plus leger"); exceptions.forEach(exception::addSuppressed); return exception; } public Composant getComposant() throws Exception { return composants.stream().min(Comparator.comparing(Composant::poids)) .orElseThrow(this::exceptions); } }
  • 40. Sciam 40 Scoped Values (Preview) Pour partager des objets dans le code exécuté par un thread Historiquement depuis Java 1.2, on utilise une variable de type ThreadLocal L’API ScopedValue tente de remédier à ces inconvénients Mais cela présente plusieurs risques : Mutable, fuite de mémoire, consommation de ressources public final static ScopedValue<String> VALEUR = ScopedValue.newInstance(); En incubation dans Java 20 (JEP 429) En preview dans Java 21 (JEP 446) Création d’une instance généralement statique et publique Stocker et de partager des données immuables Pour une durée de vie limitée à des traitements du thread qui les a écrits
  • 41. Sciam 41 Scoped Values (Preview) where() pour définir une valeur, chainable pour plusieurs valeurs run() : sous la forme d’une implémentation de Runnable Pour exécuter une tâche dans le thread courant String valeur = ScopedValue.where(VALEUR, "test") .<String>call(monService::traiter); System.out.println((VALEUR.isBound() ? VALEUR.get() : "non definie")); ScopedValue.where(VALEUR, "test").run(() -> { afficherValeur(); }); get() pour obtenir la valeur ou lève une NoSuchElementException isBound() pour savoir si une valeur est associée au thread Ou call() : sous la forme d’une implémentation de Callable
  • 42. Sciam 42 Scoped Values (Preview) Réassociation d’une valeur pour un traitement sous-jacent Partage avec les threads virtuels d’une StucturedTaskScope ScopedValue.where(VALEUR, "test", () -> { try (var scope = new StructuredTaskScope<String>()) { afficherValeur(); scope.fork(monServiceA::traiter); scope.fork(monServiceB::traiter); scope.joinUntil(Instant.now().plusSeconds(10)); } catch (InterruptedException | TimeoutException e) { e.printStackTrace(); } }); ScopedValue.where(VALEUR, "test").run(() -> { afficherValeur(); // test ScopedValue.where(VALEUR, "test-autre").run(monService::traiter); // test-autre afficherValeur(); // test });
  • 43. Les évolutions dans les API de Java Core Sequenced Collections Evolutions diverses
  • 44. Sciam 44 Sequenced collections L’API Collections propose des collections ordonnées Mais n’est pas homogène dans les fonctionnalités proposées Chacune avec leurs méthodes, dont certaines pas évidentes ou inexistantes Les opérations liées à l'ordre de parcours sont soit incohérentes soit absentes Des implémentations permettent d'obtenir le premier ou le dernier élément Idem pour le parcours dans l’ordre inverse Premier élément Dernier élément List list.get(0) list.get(list.size() - 1) Deque deque.getFirst() deque.getLast() SortedSet sortedSet.first() sortedSet.last() LinkedHashSet linkedHashSet.iterator().next() Introduire 3 nouvelles interfaces Pour représenter des collections avec un ordre de parcours défini SequencedCollection, SequencedSet et SequencedMap
  • 45. Sciam 45 Sequenced collections Elles possèdent : Des éléments parcourables du premier au dernier élément dans un certain ordre Fournissent des API uniformes pour accéder au 1er et dernier élément, Ajouter / supprimer le premier / dernier élément Et pour parcourir ses éléments dans l'ordre inverse
  • 46. Sciam 46 La classe java.net.http.HttpClient Implémente l’interface AutoClosable Plusieurs méthodes ont été ajoutées pour gérer l’arrêt : boolean awaitTermination(Duration) Bloque jusqu'à ce que toutes les opérations soient terminées ou jusqu'à ce que la durée soit écoulée void close() boolean isTerminated() void shutdown() Demande un arrêt ordonné des requêtes précédemment soumises Aucune nouvelle requête n'est acceptée void shutdownNow() try (var client = HttpClient.newHttpClient()) { // utilisation du client }
  • 47. Sciam 47 Les évolutions dans les autres API Ajout dans les classes java.lang.StringBuffer et StringBuilder repeat(CharSequence, int) et repeat(int, int) Ajout dans la classe java.lang.String int indexOf(String str, int beginIndex, int endIndex) int indexOf(int ch, int beginIndex, int endIndex) String[] splitWithDelimiters(String regex, int limit) Agit comme la méthode split() mais renvoie aussi le délimiteur Ajout dans la classe java.lang.Math et StrictMath Surcharges de clamp(valeur, min, max) la valeur retournée est comprise entre min et max Pour les types primitifs double, float, long et int Ajout dans la classe java.util.regex.Pattern String[] splitWithDelimiters(CharSequence input int limit) Agit comme la méthode split() mais renvoie aussi le délimiteur
  • 48. Sciam 48 Les évolutions dans les autres API Ajout de méthodes dans la classe java.utils.Collections newSequencedSetFromMap(SequencedMap) unmodifiableSequencedCollection(SequencedCollection) unmodifiableSequencedMap(SequencedMap) unmodifiableSequencedSet(SequencedSet) void shuffle(List<?>, RandomGenerator) : surcharge avec RandomGenerator Ajout de méthodes dans la classe java.util.Locale Stream<Locale> availableLocales() String caseFoldLanguageTag(String) pour formatter le code langue selon la RFC5646 String langue = Locale.caseFoldLanguageTag("fr-fr"); // fr-FR var randomizer = RandomGenerator.getDefault(); Collections.shuffle(liste,randomizer); Ajout de méthodes dans la classe java.lang.Character pour le support des Emojis
  • 49. Les évolutions dans la JVM HotSpot Generational ZGC Deprecate the Windows 32-bit x86 Port for Removal Prepare to Disallow the Dynamic Loading of Agents
  • 50. Sciam 50 Generational ZGC Rendre le ramasse-miettes ZGC générationnel Activation avec les options : -XX:+UseZGC -XX:+ZGenerational Tout en maintenant les caractéristiques actuelles ZGC générationnel devrait être une meilleure solution Pour la plupart des cas d'utilisation que le ZGC non générationnel Les tailles de tas allant de quelques centaines de Mo à 16 To Les temps de pause ne doivent pas dépasser 1 milliseconde
  • 51. Sciam 51 Les évolutions dans G1 Durant les Full GC, autorisation des objets volumineux à être déplacés Permet de réduire les risques d’OOME liés à la fragmentation des régions Le « Hot Card Cache » a été retiré Permet de réduire la consommation de mémoire native de 0.2% Durant les full GC, amélioration du compactage La fonction de GC préventifs a été complètement retirée Le tear down et set up des TLABs par thread ont été parallélisés Pour réduire les temps de pauses avec beaucoup de threads
  • 52. Sciam 52 Prepare to Disallow the Dynamic Loading of Agents Le but est d’émettre un avertissement Lorsqu’un agent est chargé dynamiquement dans une JVM en cours d'exécution Utilisation l'option -XX:+EnableDynamicAgentLoading de la JVM Pour permettre de charger dynamiquement des agents sans avertissement Pour préparer les utilisateurs à une future version Qui interdira le chargement dynamique des agents par défaut Sauf pour les outils de maintenance L’option -Djdk.instrument.traceUsage de la JVM Affiche un message et une stacktrace Lors de l’invocation de l’API java.lang.Instrument Facilite l’identification les bibliothèques Qui utilisent des agents chargés dynamiquement
  • 53. Les évolutions dans les outils du JDK
  • 54. Sciam 54 Les vues JFR (views) Support des vues JFR Qui permettent d’afficher une agrégation d’événements La commande JFR.view de la commande jcmd sur une JVM Par défaut les 10 dernières minutes ou les derniers 32Mo Modifiable avec les options les options maxage et maxsize Sur un enregistrement : l’option view de la commande jfr Plusieurs options de formatage +/- 70 vues prédéfinies sont proposées hot-methods, gc-pauses, pinned-threads, gc, memory-leaks-by-class, ... Pour obtenir la liste des vues : jcmd <pid> JFR.view ou jfr view jfr view [--verbose] [--width <integer>] [--truncate <mode>] [--cell-height <integer>] <view> <file> jcmd <pid > JFR.view <view>
  • 55. Sciam 55 Erreur si plusieurs ; entre imports Avant Java 21 Le compilateur javac autorise d’avoir plusieurs ; entre des imports Java 21 Le compilateur javac interdit d’avoir plusieurs points-virgules entre imports C:>javac MaClasse.java MaClasse.java:1: error: extraneous semicolon import java.util.List;; ^ 1 error import java.util.List;; import java.util.Set; class MaClasse { } C:>javac MaClasse.java C:>
  • 56. Les évolutions dans la sécurité L’API Key Encapsulation Mecanism
  • 57. Sciam 57 La sécurité Mises à jour de certificats racine des CA dans le keystore cacert Support de l’algorithme de signature standard "HSS/LMS" (RFC 8554) Augmentation de la taille par défaut des clés de plusieurs algorithmes L'implémentation du KeychainStore de macOS Expose maintenant les certificats avec une confiance appropriée Dans le domaine de l'utilisateur, le domaine de l'administrateur, ou les deux Les options -genseckey et –importpass de keytool affichent un warning Lorsque l'option –keyalg utilise des algo basés sur des mots de passe faibles Suppression de l’API ContentSigner Et des options -altsigner et –altsignerpath de l’outil jarsigner
  • 58. Sciam 58 L’API Key Encapsulation Mecanism KEM permet l’échange d’une clé symétrique partagée sécurisée En utilisant les API du package javax.crypto KeyPairGenerator kpg = KeyPairGenerator.getInstance("X25519"); KeyPair kp = kpg.generateKeyPair(); Un générateur d’une paire de clé (publique/privée) KEM kemSender = KEM.getInstance("DHKEM"); KEM.Encapsulator sender = kemSender.newEncapsulator(kp.getPublic()); KEM.Encapsulated encapsulated = sender.encapsulate(); // envoyer le byte[] retourné par encapsulated.encapsulation() KEM kemReceiver = KEM.getInstance("DHKEM"); KEM.Decapsulator receiver = kemReceiver.newDecapsulator(kp.getPrivate()); SecretKey sharedKey = receiver.decapsulate(encapsulated.encapsulation()); Une fonction d’encapsulation qui utilise la clé publique pour chiffrer Une fonction de désencapsulation qui utilise la clé privée pour déchiffrer Configuration possible des algorithmes de génération de clé et de chiffrement
  • 59. Les fonctionnalités du projet Panama Foreign Function & Memory API (Third Preview) Vector API (Sixth Incubator)
  • 60. Sciam 60 Foreign Function & Memory API (Third Preview) Historiquement, fusion de 2 JEPs introduites en incubation : Foreign-Memory Access API en Java 14 (JEP 370, 383, et 393) Et Foreign Linker API en Java 16 (JEP 389) Proposée en incubation en Java 17 (JEP 412) et Java 18 (JEP 419) Attention : cette API évolue beaucoup Dans chacune des versions de Java où elle est proposée Proposée en preview en Java 19 (JEP 424), 20 (JEP 434) et 21 (JEP 442) Elle est maintenant dans le package java.lang.foreign du module java.base API de bas niveau pour de manière simple, sûre et efficace : • Accéder à des données en mémoire hors du tas (off heap memory) Alternative à certaines fonctionnalités De java.nio.ByteBuffer (pas performante mais sûre) Et sun.misc.Unsafe (non standard) • Invoquer des fonctions natives Une future alternative à l’API JNI présente depuis Java 1.1
  • 61. Sciam 61 Foreign Function & Memory API (Third Preview) Requière une bonne connaissance des mécanismes natifs De bas niveau utilisés (adresses, segments, formats des données,…) Définit des classes et des interfaces permettant : D’appeler des fonctions étrangères : SymbolLookup, FunctionDescriptor, Linker De contrôler l'allocation et la désallocation de la mémoire étrangère : MemorySegment, SegmentAllocator, Arena De manipuler et accéder à la mémoire étrangère structurée : MemoryLayout et dérivées En utilisant des fonctionnalités d’Invoke Dynamique VarHandle pour l’accès accès aux données MethodHandle pour l’invocation de méthodes Autoriser les appels natifs --enable-native-access=ALL-UNNAMED Pour éviter un warning et dans le futur une erreur pour des raisons de sécurité
  • 62. Sciam 62 Foreign Function & Memory API (Third Preview) Contrôle de l'allocation et la désallocation de la mémoire étrangère L’interface Arena qui hérite de SegmentAllocator Gère l’accès à la mémoire native et leur isolation dans une portée Et s’assure que les segments alloués seront libérés et qu’il n’y a pas d’accès après libération 4 Arena proposées : global arena : une seule, partagée par tous les threads, Libération des segments à l’arrêt de la JVM Arena arena = Arena.global(); automatic arena : gérée par le ramasse-miettes, Utilisable par plusieurs threads, Libération des segments lorsqu’il n’y a plus de référence Arena arena = Arena.ofAuto(); confined arena : les segments sont libérés à la fermeture De l’arena, utilisable que par le thread qui l’a créé try (Arena arena = Arena.ofConfined()) { . . . } shared arena : combine un confined arena Avec une utilisation multi-threads Arena arena = Arena.ofShared();
  • 63. Sciam 63 Foreign Function & Memory API (Third Preview) MemorySegment décrit une zone de mémoire off heap Allocation avec surcharges de méthodes de SegmentAllocator Arena::allocate(long) Alloue un segment dont la taille en octets est précisée Notamment : Arena::allocateFrom(String) Helper qui alloue un segment pour une chaîne en UTF-8 au format C Arena::allocate(MemoryLayout) Pour allouer un segment correspondant à une structure MemorySegment chaineNative = arena.allocateFrom("Java"); MemorySegment segment = arena.allocate(100);
  • 64. Sciam 64 Foreign Function & Memory API (Third Preview) Manipulation et accès à la mémoire étrangère structurée MemoryLayout définit la structure de mémoire d'un type spécifique ValueLayout hérite de MemoryLayout Définit la façon dont les types de base sont stockés en mémoire Exemple : ValueLayout.JAVA_LONG décrit le type Java primitif long ValueLayout.ADDRESS décrit une adresse mémoire du matériel sous-jacent try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocate(10 * 4); // allouer pour 10 entiers de type int (4 octets) for (int i = 0 ; i < 10 ; i++) { segment.setAtIndex(ValueLayout.JAVA_INT, i, i); } }
  • 65. Sciam 65 Foreign Function & Memory API (Third Preview) SequenceLayout hérite de MemoryLayout Décrit un tableau d'un type spécifique décrit par un MemoryLayout StructLayout hérite de MemoryLayout Décrit une structure de types qui se suivent en mémoire // tableau de 10 doubles Java MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_DOUBLE); // un tableau composé de trois tableaux de dix tableaux entiers MemoryLayout.sequenceLayout(3, MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT)); StructLayout dateStruct = MemoryLayout.structLayout( ValueLayout.JAVA_SHORT.withName("annee"), ValueLayout.JAVA_SHORT.withName("mois"), ValueLayout.JAVA_SHORT.withName("jour"));
  • 66. Sciam 66 Foreign Function & Memory API (Third Preview) Manipulation de bas niveau d’un segment Manipulation d’un segment structuré avec des VarHandles MemorySegment segment = arena.allocate((long)(2 * 4 * 10), 1); // 10 * 2 * 4 octets (int) for (int i = 0; i < 10; i++) { segment.setAtIndex(ValueLayout.JAVA_INT, (i * 2), i); // x segment.setAtIndex(ValueLayout.JAVA_INT, (i * 2) + 1, i); // y } SequenceLayout pointsLayout = MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(ValueLayout.JAVA_INT.withName("x"), ValueLayout.JAVA_INT.withName("y"))); VarHandle xHandle = pointsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("x")); VarHandle yHandle = pointsLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("y")); MemorySegment segment = arena.allocate(pointsLayout); for (int i = 0; i < pointsLayout.elementCount(); i++) { xHandle.set(segment, (long) i, i); yHandle.set(segment, (long) i, i); }
  • 67. Sciam 67 Foreign Function & Memory API (Third Preview) Recherche et description de fonctions étrangères Chargement optionnel de la bibliothèque native par le ClassLoader Avec System::loadLibrary L’interface FunctionDescriptor Décrit les paramètres entrants et sortants d'une fonction native L’interface SymbolLookup Pour localiser et rechercher l’adresse de fonctions dans les bibliothèques natives Des fabriques pour obtenir une instance : static SymbolLookup loaderLookup() pour une instance sur les bibliothèques chargées par le ClassLoader static SymbolLookup libraryLookup(String, Arena) et libraryLookup(Path, Arena) pour une instance sur une bibliothèque qui sera chargée au besoin Lors de l'appel de fonction native avec un MethodHandle s'assure que les types Java transférés sont convertis en types C corrects et que la valeur de retour est convertie d'un type C vers le type de retour Java
  • 68. Sciam 68 Foreign Function & Memory API (Third Preview) Obtenir un éditeur de liens spécifique à l’Application Binary Interface Une ABI est un ensemble de conventions d’appel et de types de données Associés au compilateur, au système d’exploitation et au processeur utilisés La méthode defaultLookup() renvoie un SymbolLookup Pour rechercher une fonction dans un ensemble de bibliothèques courantes Exemple : libc sous Linux L’interface Linker Crée un éditeur de liens spécifique à l’ABI de la plate-forme native sous-jacente Optimisé pour les conventions d'appel de nombreuses plateformes : Linux/x64 Linux/AArch64 Linux/RISC-V Linux/PPC64 Linux/s390 macOS/x64 macOS/AArch64 Windows/x64 Windows/AArch64 AIX/ppc64 Prend en charge les conventions d'appel des autres plateformes en déléguant à libffi Linker linker = Linker.nativeLinker(); SymbolLookup stdlib = linker.defaultLookup();
  • 69. Sciam 69 Foreign Function & Memory API (Third Preview) Appel à des fonctions étrangères via des MethodHandles Linker permet une interaction aller-retour Entre le code Java et les fonctions étrangères Les appels descendants (downcall) Appels du code Java vers le code natif Les appels ascendants (upcall) Appels du code natif vers le code Java MethodHandle strlen = linker.downcallHandle( linker.defaultLookup().find("strlen").get(), FunctionDescriptor.of(JAVA_LONG, ADDRESS) ); try (Arena arena = Arena.ofConfined()) { MemorySegment chaine = arena.allocateFrom("Java"); long len = (long) strlen.invoke(chaine); }
  • 70. Sciam 70 Foreign Function & Memory API (Third Preview) Exemple Java 21 sous Windows public class DialogFFM { public static void main(String[] args) { try { System.loadLibrary("user32"); Optional<MemorySegment> msgBoxFunction = SymbolLookup.loaderLookup().find("MessageBoxA"); FunctionDescriptor msgBoxFunctionDesc = FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS, ADDRESS, JAVA_INT); Linker linker = Linker.nativeLinker(); MethodHandle methodHandle = linker.downcallHandle(msgBoxFunction.get(), msgBoxFunctionDesc); try (Arena offHeap = Arena.ofConfined()) { MemorySegment cStringMessage = offHeap.allocateUtf8String("Voulez-vous utiliser Java 21 ?"); MemorySegment cStringTitre = offHeap.allocateUtf8String("Confirmation"); int bouton = (int) methodHandle.invoke(NULL, cStringMessage, cStringTitre, 36); } } catch (Throwable t) { t.printStackTrace(); } } }
  • 71. Sciam 71 Vector API (Sixth Incubator) Exprimer des calculs vectoriels Qui, au moment de l'exécution, sont systématiquement compilés Avec les meilleures instructions vectorielles possibles sur l’architectures CPU Les SIMD sur les CPU supportées : x64 (SSE et AVX) et AArch64 (Neon) Single Instruction, Multiple Data Traiter en parallèle un tableau de données, sans threads Pour appliquer une même opération sur plusieurs valeurs traitées En un seul cycle de traitement CPU En incubation en Java 16 (JEP 338), 17 (JEP 414), 18 (JEP 417), 19 (JEP 426), 20 (JEP 438) et 21 (JEP 448) Dans le module jdk.incubator.vector
  • 72. Sciam 72 Vector API (Sixth Incubator) API plutôt de bas niveau, verbeuse, dépendant partiellement du CPU Mais qui peut offrir de meilleures performances que le code scalaire équivalent Exemple Java 21 static float[] calculerScalaire(float[] a, float[] b) { float[] c = new float[a.length]; for (int i = 0 ; i < a.length ; i++) { c[i] = a[i] * a[i] - b[i] * b[i]; } return c; } static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED; static float[] calculerVectoriel(float[] a, float[] b) { float[] c = new float[a.length]; int i = 0; for (; i < SPECIES.loopBound(a.length) ; i += SPECIES.length()) { var va = FloatVector.fromArray(SPECIES, a, i); var vb = FloatVector.fromArray(SPECIES, b, i); var vr = va.mul(va).sub(vb.mul(vb)); vr.intoArray(c, i); } for (; i < a.length; i++) { c[i] = a[i] * a[i] - b[i] * b[i]; } return c; }
  • 74. Sciam 74 Conclusion Java poursuit son évolution en respectant son modèle de releases La syntaxe, particulièrement le pattern matching Java 21 proposent des fonctionnalités concernant N’hésitez pas à télécharger un JDK 21 Cela permettra à Java de rester pertinent aujourd’hui et demain La programmation parallèle et concurrente Une meilleure utilisation du matériel moderne Pour utiliser cette version LTS du JDK