Les changements et améliorations apportés avec Java 10
Java 10 est sortie en mars 2018, c'est une version mineure par rapport aux versions précédentes.
Je vais aborder dans cet article les quelques changements apportées par cette version.
Le nouveau système de versioning
A partir de java 10 oracle a promis de faire des versions toutes les 6 mois (release train) et une Feature Release toutes les deux à trois ans. Les versions intermédiaires auront un support de 6 mois et les Feature versions LTS(Long Term Support) auront un support de 3 ans. Le but de cette approche est de pouvoir livrer fréquemment de nouvelles fonctionnalités prêtes. Ce qui permet de limiter ainsi le gap qui peut exister entre les versions majeures.
LVTI : Local-Variable Type Inference (JEP 286)
C'est une des grandes nouveaté apportées par Java 10, maintenant on peut utiliser le mot clé var pour définir des variables locales lorsque le type peut être inférer par le compilateur,
L'utilisation de var est limitée dans les cas suivant :
- Les boucles pour déclarer des variables locales ou utiliser entant qu'index
- Dans un bloc try-with-resource
- La déclaration de variable avec initialisation sur une même ligne.
Cette fonctionnalité permet de simplifier la déclaration et l'instanciation simultané des variables, ce qui rend le code plus concis.
//Exemple
Var set = new HashSet<String>() ;
//Exemple :
var numbers = List.of(1, 2, 3, 4, 5,6,7,8,9); // inferred value ArrayList<String>
// Indexe pour une boucle améliorée.
for (var number : numbers)
System.out.println(number);
// variable locale dans une boucle
for (var i = 0; i < numbers.size(); i++)
System.out.println(numbers.get(i));
Collections : nouvelles méthodes de Copy
Les méthodes List.copyOf(), Set.copyOf(), et Map.copyOf(), permettent de retourner une collection immuable.
Si la collection existante était déjà une collection immuable, elle serait retournée directement, sinon une nouvelle collection immuable sera créée.
Amélioration des collectors : Collectors to unmodifiable List
Les Streams introduit avec Java 8 ont été améliorer pour renvoyer des collections immuables avec les méthodes ci -dessous
- Collectors.toUnmodifiableList()
- Collectors.toUnmodifiableSet()
- Collectors.toUnmodifiableMap(keyFunc, valueFunc)
- Collectors.toUnmodifiableMap(keyFunc, valueFunc, mergeFunc)
Méthodes : Optional.orElseThrow()
La méthode orElseThrow() permet de renvoyer l'élément si est présent, une exception sera lancée dans le cas où l'optional est vide. Le nom de cette méthode est plus clair que la méthode get() qui lance également une exception si l'optionnal est vide mais certains développeurs ont tendance à l'oublier.
La méthode Reader.transferTo(Writer)
Permet de transférer tous les caractères d’un Reader à un Writer
//Exemple :
public class ReaderToWriter
public static void main(String[] args) {
Reader reader = new FileReader("from.txt");
Writer writer = new FileWriter("to.txt");
reader.transferTo(writer);
}
Thread-Local Handshakes (JEP 312)
L'opérations de handshake est un callback qui s'exécute pour chaque thread java lorsque ce thread est dans un Safepoint. Par conséquent ce mécanisme permet d'optimiser la phase de Safepoint global de la JVM (Hotspot).
On appelle Safepoint le mécanisme d'arrêter les processus de la JVM pour effectuer certains types d'opérations tels que :
- Pause du Garbage collection
- Désoptimisation du code
- Flush du code en cache
- Redéfinition des classes (ex hot swap ou instrumentation)
- Gestion des locks
- Diverses opérations de debug (ex. Vérification du Deadlock, dump des piles d'exécution)
L'arrêt de tous les processus est nécessaire s'assurer ce que l'initiateur du Safepoint à un accès exclusif aux structures de données de la JVM.
Ce qui permet d'effectuer des opérations comme déplacer des objets du Heap ou remplacer le code des méthodes encours d'exécution (On-Stack-Replacement)
Pendant la phase de Safepoint, tous les processus qui exécute du code java sont arrêté, les processus natifs peuvent continuer leur exécution lorsqu'il n’interagit pas avec la JVM (c.-à-d. n'essayer d'accéder à des objets via le JNI (Java Naming Interface), appeler des méthodes java).
Initialement implémenté pour les architectures x64 and SPARC, cette fonctionnalité n'est pas accessible à toutes les architectures.
Pour plus d’info : http://openjdk.java.net/jeps/312
Experimental Java-Based JIT Compiler (JEP 317)
Cette fonctionnalité permet d'utiliser le compilateur expérimental JIT Graal, comme compilateur sur les systèmes linux/64 bit.
Graal est un compilateur écrit en Java qui a été introduit par Java 9, c'est une alternative au compilateur JIT (Just in time) C1 écrit en c++, il est utilisable sous forme de plugin et peut être plugger dynamiquement à la JVM et peut être remplacer par un autre compilateur compatible JVMLCI (Java-Level JVM Compiler Interface).
Graal supporte la compilation anticipé (Ahead Of Time, AOT) qui permet de compiler le code java vers un langage machine cible et par conséquent permet d'optimiser le l'exécution de code.
Il supporte également la JSR 223, qui fournit les API de Scripting, permettant le développement de certaines fonctionnalités avec un langage de script(JavaScript, python) (Polyglot language interpretation).
Graal est encore dans la phase expérimentale, il peut être utilisé pour les tests et le débogage, pour les prochaines étapes l'objectif est pouvoir l'utiliser pour le JDK.
On peut utiliser le compilateur graal avec les options ci-dessous
-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler
Application Class-Data Sharing (JEP 310)
Cette fonctionnalité permet de réduire le temps de démarrage et l'empreinte mémoire en partageant les métadonnées des classes communs à travers différents processus java dans un fichier archive. Ce qui permet à la JVM de ne pas recréer des classes déjà présentes dans l’archive
Elle est construite sur l'infrastructure du CDS existant (introduit avec JDK 5) pour permettre de charger les classes d'application dans archive partagé.
Au démarrage, JVM effectue des étapes préliminaires notamment charger les classes en mémoire; si plusieurs JAR contiennent plusieurs classes on peut constater que le temps de création et de chargement est particulièrement long (ce qui est le cas notamment pour application qui n'utilise pas de server dédié ex Springboot).
Voici les étapes pour utiliser cette fonctionnalité :
A) déterminer la classe à archiver avec la commande ci-dessous
$java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=hello.lst -cp hello.jar HelloWorld
B) créer l'archive AppCDS: avec la commande ci-dessous
$java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=hello.lst -XX:SharedArchiveFile=hello.jsa -cp hello.jar
C) Utiliser l’archive precedent :
$java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=hello.jsa -cp hello.jar HelloWorld
Parallel Full GC for G1 (JEP 307)
Cette fonctionnalité est le changement le plus important en termes de performance apporté avec java 10.
Le Garbage collection G1 a été initialement introduit par Java 9, il est par défaut utilisé pour libérer la mémoire de la JVM. Dans son fonctionnement classique, si le GC G1 n’arrive plus à nettoyer assez rapidement la Heap de manière concurrente, il déclenche un Full GC qui était mono-thread.
Avec Java 10 l’implémentation du Garbage collection G1 a été modifié pour être exécuter en multi-threadé, on peut définir le nombre de threads avec l'option :
-XX:ParallelGCThreads
Plus plus d'infos : http://openjdk.java.net/jeps/307
Heap Allocation on Alternative Memory Devices (JEP 316)
Permet à la JVM d'allouer de mémoire Heap à partir d'un support tel qu’une carte mémoire NV-DIM
Cette fonctionnalité peut être utilisé Par Des systèmes qui sont équipé d'architectures mémoire hétérogènes.
On peut également constater que les nos applications ont toujours besoin de plus en plus de mémoire avec le big data et les base en mémoire par exemple. Avec des supports de stockage moins chère et performante (3D XPoint de Intel), on peut utiliser cette fonctionnalité pour démarrer une JVM en utilisant un espace mémoire externe.
Pour plus d'infos : http://openjdk.java.net/jeps/316