Une introduction à ce qu'il est possible de mesurer à l'aide de l'utilitaire jcmd
embarqué dans les JDK.
Tout est dans le titre. Je suis en train de comparer plusieurs choses actuellement :
-
Le surcoût que représente la JRE 17 sur une application Kotlin.
-
Le gain qu'apporte une JRE 17 custom produite à l'aide de l'utilitaire JLink.
-
Les performances de (1) et (2) face à la même application codée en Rust, en termes de consommation mémoire et d'opérations par seconde.
-
Les nouvelles options que la JRE prends en paramètre et leurs effets sur le CPU, la mémoire et le débit.
Le tuning de JRE a toujours été compliqué, mais pour obtenir une JRE de 30 Mo optimisée comme il faut, il y a encore plus de choses à connaître et comprendre qu'avant. Damned !
Résultats surprenant mais expliqués.
En substance, sur les applications codées pour le benchmark, si Java consomme beaucoup plus de mémoire que Rust (entre x3 et x10 en fonction que nous soyons sur Hotpot ou GraalVM), Java est globalement plus rapide que Rust.
Ce résultat qui paraît contre intuitif s'explique par les optimisations que la JRE effectue au runtime, ce qu'un compilateur ne peut pas faire.
Par contre, les temps de démarrage de Rust sont bien rapides que ceux de Java Hotspot mais comparable à GraalVM.
Enfin, si GraalVM consomme 2 à 3 fois moins de mémoire que Java Hotspot (mais toujours plus que Rust), ses performances sont moindre car la compilation native empêche d'effectuer les optimisations au runtime.
En dehors des temps de chargements qui sont forcément plus longs en Java, il faudrait voir ce que la compilation native au runtime pourrait apporter avec GraalVM + Truffle + Substrate VM.
Donc si vous avez :
- Beaucoup de RAM
- Pas de problème avec le temps de démarrage
- Un besoin de grosses perfs sur les requêtes de type I/O
Alors Java est le choix à privilégier sur Rust et ce résultat est totalement contre-intuitif ! /O\ #Bluffée
Alors la situation est un poil plus compliquée que ce qui est dit.
Sur les vieilles versions de Java, Oracle fait effectivement la chasse aux licences. Mais il s'agit d'un modèle privateur donc normal.
Sur les versions 8 à 19 de Java, le modèle est basé sur trois composantes. Une JVM (machine virtuelle Java) avec les dernières features, une JVM avec les derniers correctifs de stabilité ou de sécurité, une JVM gratuite.
Quelle est l'astuce ? Simple, on ne peut pas avoir ces trois composantes simultanément.
C'est-à-dire que soit on veut avoir les dernières features et être stable/securisé et alors on paie.
Soit on ne paie pas mais dans ce cas on a :
- Soit les dernières features sur une JVM instable/non-sécurisée
- Soit on a une JVM avec 3 ans de retard sur les features mais qui est gratuite et stable/sécurisée.
Enfin, rappellons que Java est libre (GPLv2) et que les projets Eclipse Temurin, Apache et OpenJDK ont fusionné pour donner naissance à une JVM 100% libre Adoptium. Donc bye bye Oracle.
Remarque : si vous codez en Kotlin, puisque le compilateur peut cibler la version de la JVM que vous utilisez et vous fournir les dernières features sans que la JVM ne soit a jour, alors autant partir sur la dernière LTS gratuite de Java et être tranquille côté sécurité / stabilité.
Sinon Kotlin se compile très bien en WASM et en natif aussi <3
Comme le site adoptium.net ne permet de ne récupérer que la dernière version du JDK 11, voici le lien vers la page des releases publiées sous GitHub.
Edit pour les autres LTS, par exemple la 17, transformer l'URL en https://github.com/adoptium/temurin17-binaries/releases/
Au temps pour moi ! Il y a cette page.
En un exemple court et simple :
package com.memorynotfound;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
public class GetUpTime {
public static void main(String... args) throws InterruptedException {
RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
System.out.println("Up time: " + rb.getUptime() + " ms");
}
}
Edit
En complément il y a ce projet sur lequel jcefmaven est basé.
Problème
- J'ai besoin de développer un client lourd.
- Je préfère mourir que de me remettre à Swing ou d'utiliser JFX (dont la réputation d'API instable avait fait le tour du web il y a quelques années).
Solution
Cette dépendance Maven qui intègre un moteur de rendu WebKit :
<dependency>
<groupId>me.friwi</groupId>
<artifactId>jcefmaven</artifactId>
<version>105.3.36</version>
</dependency>
Avec le tuto pour Java
//Create a new CefAppBuilder instance
CefAppBuilder builder = new CefAppBuilder();
//Configure the builder instance
builder.setInstallDir(new File("jcef-bundle")); //Default
builder.setProgressHandler(new ConsoleProgressHandler()); //Default
builder.addJcefArgs("--disable-gpu"); //Just an example
builder.getCefSettings().windowless_rendering_enabled = true; //Default - select OSR mode
//Set an app handler. Do not use CefApp.addAppHandler(...), it will break your code on MacOSX!
builder.setAppHandler(new MavenCefAppHandlerAdapter(){...});
//Build a CefApp instance using the configuration above
CefApp app = builder.build();
Voici le résumé :
- all to suppress all warnings
- boxing to suppress warnings relative to boxing/unboxing operations
- cast to suppress warnings relative to cast operations
- dep-ann to suppress warnings relative to deprecated annotation
- deprecation to suppress warnings relative to deprecation
- fallthrough to suppress warnings relative to missing breaks in switch statements
- finally to suppress warnings relative to finally block that don’t return
- hiding to suppress warnings relative to locals that hide variable
- incomplete-switch to suppress warnings relative to missing entries in a switch statement (enum case)
- nls to suppress warnings relative to non-nls string literals
- null to suppress warnings relative to null analysis
- rawtypes to suppress warnings relative to un-specific types when using generics on class params
- restriction to suppress warnings relative to usage of discouraged or forbidden references
- serial to suppress warnings relative to missing serialVersionUID field for a serializable class
- static-access to suppress warnings relative to incorrect static access
- synthetic-access to suppress warnings relative to unoptimized access from inner classes
- unchecked to suppress warnings relative to unchecked operations
- unqualified-field-access to suppress warnings relative to field access unqualified
- unused to suppress warnings relative to unused code
- varargs to suppress warnings about unsafe usages of variable arguments (varargs) methods, in particular, those that contain non-reifiable arguments.
Ceci se fait en deux étapes :
1) Déclarer la property dans le plugin Surefire comme suit
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>LAST_SUREFIRE_VERSION</version>
<configuration>
<systemProperties>
<property>
<name>name-the-property-will-have-in-test</name>
<value>${my-property}</value>
</property>
</systemProperties>
</configuration>
</plugin>
2) La récupérer en tant que propriété système dans les TU
val property = System.getProperty("name-the-property-will-have-in-test")
Merci à Riduidel pour le lien.
Côté Linux :
sudo apt-get install build-essential libz-dev zlib1g-dev
Côté GraalVM :
Faire pointer les variables d'environnements JAVA_HOME et GRAALVM_HOME vers le répertoire d'installation de GraalVM.
$GRAALVM_HOME/bin/gu native-image
Enjoy...
Houla je confirme !! Feather est ultra rapide et ultra petite (12 Ko).
Grâce à cette lib, j'ai abandonné tous les autres frameworks de DI, allant de Spring jusqu'à Dagger II, en passant par Guice.
Cela doit faire 4-5 ans et elle ne m'a jamais deçue.
Je suis en train de travailler sur la fabrication de JRE custom s'appuyant sur Java 17 mais cela ne servira à pas grand chose si le code que nous produisons ne déclare pas bien les modules sur lesquels il s'appuie.
Depuis Java 11, certains packages comme javax.xml.bind
ne font plus partie du JDK, ce faisant il faut les tirer en tant que dépendances soi-même.
Pour faire simple toutes les dépendances du type javax sont remplacées par des dépendances du type Jakarta, par exemple :
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
devient
<!-- Pour JEE 8 -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- Pour JEE 9 -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.0</version>
</dependency>
Ahhh Java... Je me souviens de mon tout premier code en Java. J'étais encore au lycée, je voulais apprendre à faire des sites web pour notre club d'anglais et j'avais entendu parlé de JavaScript. Alors j'ai demandé un bouquin à mes parents mais vu le prix des livres à l'époque j'avais fait une croix dessus... Puis à Noël...
J'ai reçu un libre qui s'appelait Total Java ! Et mes parents tout fiers à l'époque : "Le libraire nous a dit que tu pourrais faire des sites avec ça"... Oui avec des Applets Maman #Facepalm.
En réalité, j'ai très vite adoré Java et puis j'ai appris à le détester en migrant vers Kotlin. Le langage en lui-même n'est trop pas mal :
- Il gère la programmation par structures.
- Il gère la programmation orientée objets.
- Il gère la programmation fonctionnelle.
- Il gère la programmation par aspects.
- Il gère la programmation asynchrone.
- Il gère la programmation concurrentielle.
- Il gère programmation réactive.
- Il gère la programmation événementielle.
A présent je me rends compte de la lourdeur de sa syntaxe, que sa capacité à gérer autant de paradigmes différents font de lui un langage dont la syntaxe devient au final incohérente, que la rétro-compatibilité ascendante garantie n'a jamais permis à Java de trier le bon grain de l'ivraie pour ne se focaliser que sur le nouveau ; rétro-compatibilité depuis 1995 quand même !
@Warrior Du Dimanche si tu as des questions en Java demande, je tâcherai d'y répondre :-*. Mais quitte à bosser sur JVM, je te recommande Kotlin, bien plus élégant, bien plus expressif, bien mieux !
Excellente idée ! Merci à @Philou pour le lien.
Excellent, je cherchais un moteur Kotlin 2D sympa pour un kata un peu gros. C'est top !
Merci @Riduidel
Je suis bluffée ! Les filters des Stream sont toujours plus lents que les boucles classiques en Java.
Le seul moment où les boucles classiques ne sont pas les plus rapides, c'est dans le cas de figure où la taille des collections dépasse les 500 K éléments et que les Parallel Stream sont utilisées à la place des Stream (et que nous sommes sur une architecture multi-cœurs évidemment) ; sinon les Stream perdent à chaque fois.
Une lib Java rikiki de 153 Ko permet d'écrire des logs plus rapidement qu'avec LogBack ou Log4J2 et qui implémente les interfaces de SLF4J. #Nice
@Riduidel : la réponse à ta question est dans ce post (un peu long à lire désolée).
Un objet Iterable implique que l'on traite les données à partir d'un paradigme de programmation procédurale et impératif, c'est-à-dire ni objet, ni fonctionnel. Or les Optionals sont justement des éléments prévus spécialement pour penser et coder dans un style fonctionnel parmi les plus purs.
Vouloir que la classe Optional implémente Itérable, c'est un marqueur fort permettant d'identifier qu'une personne n'a pas encore appris et compris le paradigme fonctionnel où :
- Les instructions if/else/switch ont été remplacés par du pattern-matching.
- Les boucles for/while/do-while ont été remplacées par des appels récursifs.
- Les références ont été remplacées par des lambdas.
Enfin, le fait que Scala ait implémenté Iterable dans les Optionals est justement ce qui fait de Scala un mauvais langage fonctionnel à mon avis, car si c'est génial d'être multi-paradigme, c'est par contre très mal de mélanger les paradigmes dans les éléments constituants l'API du langage car cela créé un code extrêmement incohérent où il devient difficile de savoir comment s'exprimer ; mais c'est mon côté software craftswomanship qui parle #JavaDuchesses