Participer au site avec un Tip
Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Les outils jlink et jaotc

Les modules Java et l'outil jmod Le Garbage Collector



Accès rapide :
Utilisation de l'outil jlink
A quoi sert l'outil jlink
Aide en ligne de l'outil jlink
Création d'une image via l'outil jlink.
Démarrage d'une image.
Utilisation de l'outil jaotc
Qu'est-ce que l'« Ahead of Time Compilation »
Aide en ligne de l'outil jaotc
Compilation anticipée d'un code Java
Exécution d'une librairie produite par jaotc

ces possibilités constituent une nouveauté de la plateforme Java SE 9.0. Si vous utilisez une version antérieure de Java (c'est qui est encore très souvent le cas), vous ne pourrez pas mettre en oeuvre les exemples qui suivent. Dans ce cas, je vous conseille d'installer une version plus récente de Java. Pour rappel, plusieurs versions de Java peuvent être installées en parallèle.

Utilisation de l'outil jlink

A quoi sert l'outil jlink

JLink permet de produire une nouvelle « JVM light » dans laquelle vous allez stocker uniquement les codes des modules utilisés par votre programme : les modules non utilisés du Java SE ne seront pas injecté dans cette nouvelle JVM. Cet outil va fusionner l'ensemble des modules utilisés (modules standards Java SE, modules applicatifs et modules des librairies utilisées) en une seule archive qui permettra l'exécution de votre programme.

La nouvelle JVM produite sera appelée une image et elle sera auto-suffisante. Vous pourrez alors déployer cette image sur les postes clients sans nécessiter l'installation d'une JVM traditionnelle.

l'image produite est spécifique à une plateforme donnée (ARM, i86, i64...). Vous pouvez bien entendu créer autant d'image que de plateformes supportées par votre application.

Aide en ligne de l'outil jlink

La première étape consiste à obtenir de l'aide en ligne sur l'outil. Pour ce faire vous pouvez lancer cet outil avec l'option --help. Voici le résultat produit.

$> jlink --help
Usage: jlink <options> --module-path <modulepath> --add-modules <module>[,<module>...]
Possible options include:
      --add-modules <mod>[,<mod>...]    Root modules to resolve in addition to the
                                        initial modules. <mod> can also be ALL-MODULE-PATH.
      --bind-services                   Link in service provider modules and
                                        their dependences
  -c, --compress=<0|1|2>                Enable compression of resources:
                                          Level 0: No compression
                                          Level 1: Constant string sharing
                                          Level 2: ZIP
      --disable-plugin <pluginname>     Disable the plugin mentioned
      --endian <little|big>             Byte order of generated jimage
                                        (default:native)
  -h, --help, -?                        Print this help message
      --ignore-signing-information      Suppress a fatal error when signed
                                        modular JARs are linked in the image.
                                        The signature related files of the
                                        signed modular JARs are not copied to
                                        the runtime image.
      --launcher <name>=<module>[/<mainclass>]
                                        Add a launcher command of the given
                                        name for the module and the main class
                                        if specified  
      --limit-modules <mod>[,<mod>...]  Limit the universe of observable
                                        modules
      --list-plugins                    List available plugins
  -p, --module-path <path>              Module path.
                                        If not specified, the JDKs jmods directory
                                        will be used, if it exists. If specified,
                                        but it does not contain the java.base module,
                                        the JDKs jmods directory will be added,
                                        if it exists.
      --no-header-files                 Exclude include header files
      --no-man-pages                    Exclude man pages
      --output <path>                   Location of output path
      --post-process-path <imagefile>   Post process an existing image
      --resources-last-sorter <name>    The last plugin allowed to sort
                                        resources
      --save-opts <filename>            Save jlink options in the given file
  -G, --strip-debug                     Strip debug information
      --suggest-providers [<name>,...]  Suggest providers that implement the
                                        given service types from the module path
  -v, --verbose                         Enable verbose tracing
      --version                         Version information
      @<filename>                       Read options from file
$>

Création d'une image via l'outil jlink.

Donc, pour créer une image de votre application, vous devez utiliser l'outil jlink. Vous devez lui spécifier le(s) dossier(s) contenant les modules à importer dans l'image (paramètre --module-path), l'ensemble des modules à importer (paramètre --add-modules) et le nom de l'image à produire (paramètre --output).

attention, les modules en dépendances sont automatiquement déduits (par exemple java.base). Il n'est donc pas nécessaire de les spécifier.
$> jlink --module-path . --add-modules fr.koor.myapp --output image 
Création d'une image à partir d'un module
image correspond au nom de l'image produite. Vous auriez pu aussi utiliser le nom de votre application comme nom d'image.

Dans ce cas, la JVM associée à l'image pourra être démarrée en lançant le programme image/bin/java. Le problème est que dans ce cas, il faudra spécifier un certain nombre de paramètres lors du démarrage de la JVM : notamment le nom de la classe de démarrage.

Mais il est aussi possible de générer un autre exécutable permettant de démarrer directement le programme sur votre classe de démarrage (celle qui porte la méthode main).

$> jlink --module-path . --add-modules fr.koor.myapp --output image --launcher start=fr.koor.myapp/fr.koor.myapp.Start
Création d'une image à partir d'un module en spécifiant la classe de démarrage
le nouvel exécutable produit se nommera image/bin/start (ce qui correspond à l'information localisée en partie gauche du paramètre --launcher). Le premier fr.koor.myapp du launcher correspond au nom du module portant la classe de démarrage. Le second correspond au package (dans le module initialement spécifié) contenant la classe de démarrage (ici Start).

Démarrage d'une image.

Deux possibilités vous sont proposées pour démarrer votre image. Soit en procédant à un démarrage traditionnel d'une JVM, soit en utilisant l'exécutable personnalisé produit par jlink (grâce à l'option --launcher).

Pour démarrer la JVM de manière traditionnelle, vous devez spécifier l'option -m pour spécifier le module contenant la classe de démarrage et le nom de cette classe. Voici un exemple.

$> image/bin/java -m fr.koor.myapp/fr.koor.myapp.Start
Démarrage d'une image à partir de l'exécutable java

Si vous utilisez l'exécutable personnalisé, les choses sont bien plus simples : vous n'avez plus besoin de spécifier de paramètre, comme le montre l'exemple suivant.

$> image/bin/start
Démarrage d'une image à partir de l'exécutable personnalisé

Utilisation de l'outil jaotc

Qu'est-ce que l'« Ahead of Time Compilation »

Historiquement parlant, Java utilise un « JIT Compiler » (Just In Time Compiler). Effectivement, l'outil javac (le compilateur Java) produit du code en langage machine pour le processeur virtuel Java. Le problème, c'est que personne ne possède un tel processeur (il est virtuel). Du coup, la JVM finalise la phase de compilation durant son exécution : cela à un petit impacte sur les performances de la JVM. L'intérêt de cette approche réside dans le fait que vous livrez un programme qui peut, potentiellement, s'exécuter sur n'importe quelle architecture.

Il y a néanmoins des cas, ou l'utilisation d'un JIT Compiler pose problème : c'est notamment le cas avec les systèmes iOS, ou cette technologie est prohibée par les contrats de licence. Une autre situation ou le JIT n'est pas préconisé, c'est pour tout ce qui est système embarqué (voir mobile). Effectivement, ces systèmes fonctionnent souvent avec des batteries et finaliser la construction du programme au runtime a pour effet de consommer plus de batterie. Dans de tels cas, vous pouvez utiliser l'AOT.

Un compilateur AOT (Ahead Of Time Compiler) réalise, en quelque sorte, l'opposé d'un JIT Compiler. La traduction du code machine Java est effectuée une fois pour toute à la construction du programme et avant la première exécution de ce dernier.

a titre d'exemple, ART (Android RunTime - la JVM de la plateforme Android) utilise déjà cette technique depuis plusieurs années et est devenu la norme depuis 2014. A l'installation de l'archive de votre application (l'APK - Android PacKage), le compilateur AOT finalise, une fois pour toute, la construction du programme spécifiquement pour la plateforme utilisée (ARM, i32, i64...).

Un avantage de l'AOT est qu'on a plus une vision d'ensemble sur le code à compiler. En conséquence les optimisations produites sur le code machine sont souvent bien meilleures qu'avec un compilateur JIT.

Depuis, la version 9.0 du Java SE, un compilateur AOT expérimental est proposé dans le JDK (en Java SE 14, il est toujours proposé comme étant une fonctionnalité expérimentale). Il se nomme jaotc et il est localisé dans le répertoire bin de votre JDK.

Aide en ligne de l'outil jaotc

Vous trouverez ci-dessous l'aide en ligne proposée par l'outil jaotc.

$> jaotc --help
Usage: jaotc <options> list

  list       A : separated list of class names, modules, jar files
             or directories which contain class files.

where options include:
  --output <file>            Output file name
  --class-name <class names> List of classes to compile
  --jar <jarfiles>           List of jar files to compile
  --module <modules>         List of modules to compile
  --directory <dirs>         List of directories where to search for files to compile
  --search-path <dirs>       List of directories where to search for specified files
  --compile-commands <file>  Name of file with compile commands
  --compile-for-tiered       Generate profiling code for tiered compilation
  --compile-with-assertions  Compile with java assertions
  --compile-threads <number> Number of compilation threads to be used
  --ignore-errors            Ignores all exceptions thrown during class loading
  --exit-on-error            Exit on compilation errors
  --info                     Print information during compilation
  --verbose                  Print verbose information
  --debug                    Print debug information
  -? -h --help               Print this help message
  --version                  Version information
  --linker-path              Full path to linker executable
  -J<flag>                   Pass <flag> directly to the runtime system
$> 

Compilation anticipée d'un code Java

Vous pouvez compiler, pour la plateforme considérée, des simples fichiers .class avec jaotc.

$> jaotc --output ../libSimpleSample.so -J-cp -J./bin SimpleSample
Compilation AOT d'un fichier .class localisé dans le répertoire bin
oui, je sais, la manière de spécifier la localisation des fichiers .class est certes particulière (les doubles options -J), mais c'est la seule manière qui marche pour le moment.
sous Linux, une librairie à changement dynamique doit commencer par le préfixe lib et doit se terminer par le suffixe .so (Shared Object). D'où le nom proposé pour la librairie de code natif : libSimpleSample.so. Si vous êtes sur système Windows, une telle librairie doit se terminer par le suffixe .dll (Dynamic Linking Library). Dans ce cas, le nom de la librairie devra être SimpleSample.dll.

Vous pouvez aussi compiler des modules Java (qu'ils soient aux formats Jar ou JMod). Voici des exemples de lancement de la compilation sur des modules Java (prédéfinis ou non).

$> jaotc --output ../libjava.base.so --module java.base
Compilation AOT du module standard java.base

$> jaotc --output ../libsuperlib.so  fr.koor.superlib.jar
Compilation AOT d'un module (ici de type Jar) de votre projet

Exécution d'une librairie produite par jaotc

Pour exécuter un code compilé en AOT, vous devez utiliser l'option -XX:AOTLibrary. Imaginons le programme suivant.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 

public class SimpleSample {

    public static String doSomething() {
        System.out.println( "In doSomething" );
        return "ok";
    }
    
    public static void main(String[] args) {
        
        System.out.println( "Hello - " + Math.random() );
        System.out.println( doSomething() );
        
    }
    
}
Contenu de la classe SimpleSample.java

Voici un exemple de compilation AOT et de démarrage d'une JVM à partir de la librairie produite.

$>  jaotc --output ../libSimpleSample.so -J-cp -J./bin SimpleSample
$>  java -XX:+UnlockExperimentalVMOptions -XX:AOTLibrary=./libSimpleSample.so SimpleSample
Hello - 0.9272779396108479
In doSomething
ok
$>  
n'oubliez pas que la compilation AOT est pour le moment une fonctionnalité expérimentale. Elle n'est donc par directement supportée par la JVM. Pour activer le support expérimental, il faut donc rajouter l'option -XX:+UnlockExperimentalVMOptions.
nous l'avons dit, mais j'insiste encore une fois. La compilation AOT produit du code machine natif spécifique à une plateforme donnée. Vous perdez donc l'indépendance via à vis de la plateforme utilisée. Si vous chargez un module compilé en AOT sur une machine non compatible, une erreur d'exécution sera, bien évidemment, produite.


Les modules Java et l'outil jmod Le Garbage Collector