[JAVA] AppCDS @ OpenJDK

OpenJDK et AppCDS

Il a été annoncé lors de Java One 2017 de cette année que les fonctionnalités du référentiel fermé d'Oracle JDK seront fusionnées dans Open JDK. Dans ce cadre, AppCDS (Application Class Data Sharing) a été entré le 27/11 (28/11 à l'heure du Japon) dans OpenJDK. 78b2ecdd3c4b). Aujourd'hui, j'aimerais déplacer cette fonction et l'essayer. ~~ Quoi qu'il en soit, je veux que vous fassiez même un des plantages. ~~

Cela dit, le JDK Oracle provient déjà du JDK 8u40, donc quiconque le connaît le sait déjà.

supposition

$ hg clone http://hg.openjdk.java.net/jdk/hs
$ cd hs
$ bash configure --with-extra-cflags="-Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=maybe-uninitialized" --disable-warnings-as-errors
$ make images
$ ${PWD}/build/linux-x86_64-normal-server-release/images/jdk/bin/java -version
openjdk version "10-internal"
OpenJDK Runtime Environment (build 10-internal+0-adhoc.fedora.hs)
OpenJDK 64-Bit Server VM (build 10-internal+0-adhoc.fedora.hs, mixed mode)

Qu'est-ce que CDS (Class Data Sharing)?

(Système) Vous pouvez créer un fichier contenant des métadonnées en lecture seule d'une classe appelée «archives partagées» qui charge et vide les classes utilisées à partir du fichier JAR dans la représentation interne privée JVM. C'est un mécanisme que le temps de démarrage est raccourci car il est plus rapide de restaurer ce fichier (archive partagée) par mappage mémoire que de charger la classe à partir de 1 lors du démarrage de la JVM.

Comme son nom l'indique, cette «archive partagée» peut être partagée entre plusieurs JVM, de sorte que le partage entre plusieurs processus JVM supprime également l'encombrement (empreinte mémoire dynamique). Les archives partagées sont installées par défaut, en fonction de la façon dont le JRE est installé. Vous pouvez également le fabriquer vous-même.

CDS ciblait les fichiers JAR système, mais AppCDS ciblait également le code d'application utilisateur. Correctement, CDS n'était que la classe cible du chargeur de classe d'amorçage, mais AppCDS peut en outre archiver les classes cibles du chargeur de classe système intégré (également appelé chargeur de classe d'application), du chargeur de classe de plate-forme et du chargeur de classe personnalisé.

Essayez AppCDS

À l'origine, il est bon de voir l'effet lorsque la classe est exécutée dans un grand nombre d'applications, mais comme il n'y a rien qui puisse être facilement préparé, je l'ai créé avec la valeur par défaut de Spring Initializr Améliorons le temps de démarrage de l'application de démonstration demo-0.0.1-SNAPSHOT.jar.

La valeur par défaut ressemble à ceci:

$ java -cp target/demo-0.0.1-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.8.RELEASE)
:
2017-12-01 06:10:17.856  INFO 27529 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.103 seconds (JVM running for 1.841)
:

«1,103 secondes» est la ligne de base.

Faites une liste des classes à archiver

Pour créer une archive partagée, commencez par extraire les classes utilisées par les commandes suivantes.

$ java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=demo.list -cp target/demo-0.0.1-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher
:
2017-12-01 06:09:04.430  INFO 27451 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 320.159 seconds (JVM running for 454.536)
:

Récupère la liste demo.list des classes d'utilisation extraites lors de l'exécution de l'application. Depuis qu'il est extrait, il faut beaucoup de temps pour démarrer comme un imbécile. Le contenu est une liste de classes comme suit.

$ cat demo.list
java/lang/Object
java/lang/String
:
org/springframework/boot/loader/JarLauncher
org/springframework/boot/loader/ExecutableArchiveLauncher
:

Bien entendu, il est également possible de l'ajouter à la main.

Créer une archive partagée

Maintenant que nous avons une liste de classes, chargez-la une fois, videz-la et créez une archive partagée.

$ /home/fedora/workspace/hs/build/linux-x86_64-normal-server-release/images/jdk/bin/java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=demo.list -XX:SharedArchiveFile=demo.jsa -XX:+IgnoreUnverifiableClassesDuringDump -cp target/demo-0.0.1-SNAPSHOT.jar
narrow_klass_base = 0x0000000800000000, narrow_klass_shift = 3
Allocated temporary class space: 1073741824 bytes at 0x00000008c0000000
Allocated shared space: 3221225472 bytes at 0x0000000800000000
Loading classes to share ...
Loading classes to share: done.
Rewriting and linking classes ...
Rewriting and linking classes: done
Number of classes 1866
    instance classes   =  1789
    obj array classes  =    69
    type array classes =     8
Updating ConstMethods ... done.
Removing unshareable information ... done.
Scanning all metaspace objects ...
Allocating RW objects ...
Allocating RO objects ...
Relocating embedded pointers ...
Relocating external roots ...
Dumping symbol table ...
Dumping String objects to closed archive heap region ...
Dumping objects to open archive heap region ...
Relocating SystemDictionary::_well_known_klasses[] ...
Removing java_mirror ... done.
mc  space:      8800 [  0.0% of total] out of     12288 bytes [ 71.6% used] at 0x0000000800000000
rw  space:   5381944 [ 22.2% of total] out of   5382144 bytes [100.0% used] at 0x0000000800003000
ro  space:   9586008 [ 39.5% of total] out of   9588736 bytes [100.0% used] at 0x0000000800525000
md  space:      6160 [  0.0% of total] out of      8192 bytes [ 75.2% used] at 0x0000000800e4a000
od  space:   8581008 [ 35.3% of total] out of   8581120 bytes [100.0% used] at 0x0000000800e4c000
st0 space:    634880 [  2.6% of total] out of    634880 bytes [100.0% used] at 0x00000007bff00000
oa0 space:     81920 [  0.3% of total] out of     81920 bytes [100.0% used] at 0x00000007bfe00000
total    :  24280720 [100.0% of total] out of  24289280 bytes [100.0% used]

Récupérez l'archive partagée demo.jsa.

Vérifiez si c'est plus rapide

Vérifions s'il est devenu plus rapide en chargeant l'archive partagée que nous avons créée.

$ /home/fedora/workspace/hs/build/linux-x86_64-normal-server-release/images/jdk/bin/java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=demo.jsa -cp target/demo-0.0.1-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher
:
2017-12-01 06:20:39.487  INFO 28908 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.992 seconds (JVM running for 1.414)
:

C'est "0,992 seconde", ce qui est environ 10% plus rapide, ce qui est une bonne sensation. En termes de nombres absolus, AppCDS est plus efficace car il y a plus de classes en raison de ses caractéristiques, il peut donc être plus efficace dans les applications avec de nombreuses dépendances.

Voyez ce qui se passe vraiment

Vous pouvez vérifier si le chargement de la classe est effectué à partir de l'archive ou de la bibliothèque partagée (fichier de classe). Le chargement de classe peut être enregistré avec -Xlog: class + load = info.

AppCDS activé

Vous pouvez voir que la bibliothèque principale et la classe principale sont chargées à partir du fichier d'objets partagés.

$ /home/fedora/workspace/hs/build/linux-x86_64-normal-server-release/images/jdk/bin/java -Xlog:class+load=info -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=demo.jsa -cp target/demo-0.0.1-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher
[0.003s][info][class,load] opened: /home/fedora/workspace/hs/build/linux-x86_64-normal-server-release/images/jdk/lib/modules
[0.055s][info][class,load] java.lang.Object source: shared objects file
[0.055s][info][class,load] java.io.Serializable source: shared objects file
[0.055s][info][class,load] java.lang.Comparable source: shared objects file
:
[0.098s][info][class,load] org.springframework.boot.loader.Launcher source: shared objects file
[0.098s][info][class,load] org.springframework.boot.loader.ExecutableArchiveLauncher source: shared objects file
[0.098s][info][class,load] org.springframework.boot.loader.JarLauncher source: shared objects file
:

AppCDS désactivé

Vous pouvez voir qu'il lit à partir du fichier de bibliothèque contrairement à quand il est activé.

$ /home/fedora/workspace/hs/build/linux-x86_64-normal-server-release/images/jdk/bin/java -Xlog:class+load=info -Xshare:off -cp target/demo-0.0.1-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher
[0.003s][info][class,load] opened: /home/fedora/workspace/hs/build/linux-x86_64-normal-server-release/images/jdk/lib/modules
[0.013s][info][class,load] java.lang.Object source: jrt:/java.base
[0.014s][info][class,load] java.io.Serializable source: jrt:/java.base
[0.014s][info][class,load] java.lang.Comparable source: jrt:/java.base
:
[0.131s][info][class,load] org.springframework.boot.loader.Launcher source: file:/home/fedora/workspace/startup/demo/target/demo-0.0.1-SNAPSHOT.jar
[0.131s][info][class,load] org.springframework.boot.loader.ExecutableArchiveLauncher source: file:/home/fedora/workspace/startup/demo/target/demo-0.0.1-SNAPSHOT.jar
[0.131s][info][class,load] org.springframework.boot.loader.JarLauncher source: file:/home/fedora/workspace/startup/demo/target/demo-0.0.1-SNAPSHOT.jar
:

Résumé

Il existe d'autres petites options disponibles, mais à première vue, il semble que les fonctionnalités disponibles dans Oracle JDK arrivent, comme annoncé. Une application de démonstration simple qui utilise le framework a réduit le montant d'environ 10%, mais une application avec un plus grand nombre de classes peut être plus efficace. Que diriez-vous de l'utiliser pendant le développement?

Je me demande s'il va planter s'il est étrangement combiné avec AOT.

[Édition supplémentaire] Accélérez

/home/fedora/workspace/hs/build/linux-x86_64-normal-server-release/images/jdk/bin/java -Xlog:all=off -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:+UseSerialGC -Xverify:none -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=demo.jsa -cp target/demo-0.0.1-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher
2017-12-01 07:14:41.301  INFO 29775 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.811 seconds (JVM running for 1.178)

C'était "0,811 seconde"! Je voulais aussi essayer un petit runtime personnalisé qui supprimait complètement les modules de plate-forme inutiles avec jlink, mais je n'avais pas le temps, donc c'est tout pour aujourd'hui!

En plus de ces idées d'exécution, il existe également un moyen de maintenir la JVM en marche et en cours d'exécution pour raccourcir le temps de démarrage de la JVM. Il existe différentes approches, mais des exemples d'implémentation sont nailgun et [falchion](https: // github) qui implémente un déploiement non perturbateur en utilisant SO_REUSEPORT. Il peut être intéressant de jeter un œil à .com / kawasima / falchion).

Recommended Posts

AppCDS @ OpenJDK
Installez OpenJDK
Comment installer OpenJDK 11 Memo
Enregistreur de vol OpenJDK 11