Construisez un déambulateur en fonte sur Spring Boot et AppCDS


Partage de donnĂ©es de classe d'application (AppCDS) - Fonction JVM pour accĂ©lĂ©rer le dĂ©marrage et Ă©conomiser de la mĂ©moire. AprĂšs ĂȘtre apparu Ă  ses dĂ©buts dans HotSpot dans JDK 1.5 (2004), il est restĂ© longtemps trĂšs limitĂ©, et mĂȘme partiellement commercial. Ce n'est qu'avec OpenJDK 10 (2018) qu'il a Ă©tĂ© mis Ă  la disposition de simples mortels, tout en Ă©largissant le champ d'application. Et rĂ©cemment, Java 13 a essayĂ© de simplifier cette application.


L'idĂ©e d'AppCDS est de «partager» des classes une fois chargĂ©es entre des instances de la mĂȘme machine virtuelle Java sur le mĂȘme hĂŽte. Il semble que cela devrait ĂȘtre idĂ©al pour les microservices, en particulier les "broilers" sur Spring Boot avec leurs milliers de classes de bibliothĂšque, car maintenant ces classes n'auront plus besoin d'ĂȘtre chargĂ©es (analysĂ©es et vĂ©rifiĂ©es) Ă  chaque dĂ©marrage de chaque instance JVM, et elles ne seront pas dupliquĂ©es en mĂ©moire. Cela signifie que le lancement devrait devenir plus rapide et la consommation de mĂ©moire devrait ĂȘtre infĂ©rieure. Merveilleux, non?


Tout est ainsi, tout est ainsi. Mais si vous, l'odnokhabryanin, ne croyiez pas aux panneaux du boulevard, mais aux chiffres et aux exemples spécifiques, alors bienvenue à Kat - essayons de comprendre comment c'est vraiment ...


Au lieu de l'avertissement


Avant vous n'est pas un guide d'utilisation d'AppCDS, mais un rĂ©sumĂ© des rĂ©sultats d'une petite Ă©tude. J'Ă©tais intĂ©ressĂ© Ă  comprendre comment cette fonction JVM est applicable dans mon projet de travail, et j'ai essayĂ© de l'Ă©valuer du point de vue d'un dĂ©veloppeur d'entreprise, en exposant le rĂ©sultat dans cet article. Cela n'incluait pas des sujets tels que l'utilisation d'AppCDS sur le chemin du module, l'implĂ©mentation d'AppCDS sur d'autres machines virtuelles (pas HotSpot) et les subtilitĂ©s de l'utilisation des conteneurs. Mais il y a une partie thĂ©orique pour explorer le sujet, ainsi qu'une partie expĂ©rimentale Ă©crite pour que vous puissiez rĂ©pĂ©ter l'expĂ©rience vous-mĂȘme. Aucun rĂ©sultat n'a encore Ă©tĂ© appliquĂ© en production, mais qui sait Ă  quoi ressemblera demain ...


Théorie


Une brĂšve introduction Ă  AppCDS


La connaissance de ce sujet peut avoir eu lieu dans plusieurs sources, par exemple:


  • dans un article de Nikolai Parlog (y compris Java 13 buns, mais sans Spring Boot)
  • dans un rapport et un article de Volker Simonis (sans Java 13, mais avec des dĂ©tails)
  • dans un rapport de l' auteur de ces lignes (sans Java 13, mais en mettant l'accent sur Spring Boot)

Afin de ne pas me lancer dans une nouvelle narration, je ne soulignerai que quelques points importants pour cet article.


Tout d'abord, AppCDS est une extension de la fonction CDS qui est apparue depuis longtemps dans HotSpot, dont l'essence est la suivante:



Pour donner vie aux deux idées, vous devez procéder comme suit (en termes généraux):


  1. Obtenez une liste des classes que vous souhaitez partager entre les instances d'application
  2. Fusionnez ces classes dans une archive adaptée au mappage de la mémoire
  3. Connectez l'archive à chaque instance de l'application au démarrage

Il semblerait que l'algorithme ne soit que 3 Ă©tapes - prenez-le et faites-le. Mais ici commence la nouvelle, toutes sortes de choses.


La mauvaise chose est que dans le pire des cas, chacun de ces Ă©lĂ©ments se transforme en au moins un lancement JVM avec ses propres options spĂ©cifiques, ce qui signifie que l'algorithme entier est un jonglage subtil du mĂȘme type d'options et de fichiers. Cela ne semble pas trĂšs prometteur, n'est-ce pas?


Mais il y a une bonne nouvelle: les travaux d'amélioration de cet algorithme sont en cours , et à chaque version de Java, son application devient plus simple. Ainsi, par exemple:


  • Dans OpenJDK 10 et 11, vous pouvez ignorer l'Ă©tape 1 si vous souhaitez partager uniquement les classes JDK principales, car elles ont dĂ©jĂ  Ă©tĂ© compilĂ©es pour nous et placĂ©es dans $JAVA_HOME\lib\classlist (≈1200 pcs.).
  • Dans OpenJDK 12, vous pouvez ignorer l' Ă©tape 2 , car avec la liste des classes, l'archive de distribution comprend Ă©galement une archive prĂȘte Ă  l'emploi avec eux, qui est utilisĂ©e prĂȘte Ă  l'emploi et ne nĂ©cessite pas de connexion explicite.
  • Au cas oĂč vous souhaiteriez partager tout le reste (et le souhaitez gĂ©nĂ©ralement)
    OpenJDK 13 fournit des archives Dynamic CDS - des archives qui sont collectées pendant le fonctionnement de l'application et enregistrées lors de la dotation en personnel. Cela vous permet de réduire les points 1 et 2 en un point pas trop confus (bien que tout ne soit pas si simple, mais plus à ce sujet plus tard).

Ainsi, quel que soit le processus de préparation d'AppCDS, les 3 étapes énumérées ci-dessus sont toujours derriÚre, juste dans certains cas, elles sont voilées.


Comme vous l'avez probablement remarquĂ©, avec l'avĂšnement d'AppCDS, de nombreuses classes d'application commencent une double vie: elles vivent simultanĂ©ment dans leurs anciens emplacements (le plus souvent des fichiers JAR) et dans une nouvelle archive partagĂ©e. Dans le mĂȘme temps, le dĂ©veloppeur continue de les modifier / supprimer / complĂ©ter au mĂȘme endroit, et la JVM les reprend de la nouvelle lorsqu'elle fonctionne. Il n'est pas nĂ©cessaire d'ĂȘtre un devin pour voir le danger d'une telle situation: si rien n'est fait, tĂŽt ou tard des copies des cours se corrodent, et nous obtiendrons de nombreux charmes de «l'enfer JAR» typique. Il est clair que la JVM ne peut pas empĂȘcher les changements de classe, mais elle devrait ĂȘtre capable de dĂ©tecter un Ă©cart dans le temps. Cependant, faire cela en comparant les classes par paires, mĂȘme par des sommes de contrĂŽle, est une idĂ©e; il peut annuler le reste des gains de productivitĂ©. C'est probablement pourquoi les ingĂ©nieurs JVM n'ont pas sĂ©lectionnĂ© les classes individuelles comme objet de comparaison, mais tout le chemin de classe, et ont dĂ©clarĂ© dans la documentation AppCDS: "Le chemin de classe lors de la crĂ©ation d'une archive partagĂ©e doit ĂȘtre le mĂȘme (ou au moins un prĂ©fixe) que lors des lancements ultĂ©rieurs de l'application."


Notez que le chemin de classe utilisĂ© au moment de la crĂ©ation de l'archive doit ĂȘtre le mĂȘme que (ou un prĂ©fixe) le chemin de classe utilisĂ© au moment de l'exĂ©cution.

Mais ce n'est pas une dĂ©claration sans ambiguĂŻtĂ©, car, comme vous vous en souvenez, un chemin de classe peut ĂȘtre formĂ© de diffĂ©rentes maniĂšres, telles que:


  • lire des fichiers .class nus Ă  partir de rĂ©pertoires de packages compilĂ©s,
    par exemple java com.example.Main
  • l'analyse des rĂ©pertoires avec des fichiers JAR lors de l'utilisation de caractĂšres gĂ©nĂ©riques,
    par exemple java -cp mydir/* com.example.Main
  • liste explicite des fichiers JAR et / ou ZIP,
    par exemple java -cp lib1.jar;lib2.jar com.example.Main

(sans parler du fait que le -cp/-classpath/--class-path peut Ă©galement ĂȘtre dĂ©fini diffĂ©remment, par exemple via les options JVM -cp/-classpath/--class-path , la CLASSPATH environnement CLASSPATH ou l'attribut du fichier JAR Class-Path Ă  lancer)


Parmi ces méthodes, une seule est prise en charge dans AppCDS - énumération explicite des fichiers JAR. Apparemment, les ingénieurs HotSpot JVM ont estimé que la comparaison des chemins de classe dans l'archive AppCDS et dans l'application lancée ne serait suffisamment rapide et fiable que s'ils étaient spécifiés aussi clairement que possible - avec une liste exhaustive habituelle.


CDS / AppCDS prend en charge l'archivage des classes Ă  partir de fichiers JAR uniquement.

Il est important de noter ici que cette déclaration n'est pas récursive, c'est-à-dire ne s'applique pas aux fichiers JAR à l'intérieur des fichiers JAR (sauf s'il s'agit de Dynamic CDS, voir ci-dessous). Cela signifie que les poupées JAR habituelles émises par Spring Boot ne fonctionneront pas comme ça avec AppCDS normal, vous devrez vous asseoir.


Un autre inconvĂ©nient du travail de CDS est que les archives partagĂ©es sont projetĂ©es sur la mĂ©moire avec des adresses fixes (commençant gĂ©nĂ©ralement Ă  0x800000000 ). En soi, ce n'est pas mauvais, mais comme la randomisation de la disposition de l'espace d'adressage (ASLR) est activĂ©e par dĂ©faut sur la plupart des systĂšmes d'exploitation, la plage de mĂ©moire requise peut ĂȘtre partiellement occupĂ©e. Dans ce cas, la -Xshare HotSpot fait l’ option spĂ©ciale -Xshare qui prend en charge trois valeurs:


  • -Xshare:on - force CDS / AppCDS; si la plage est occupĂ©e, la JVM se termine avec une erreur. Ce mode n'est pas recommandĂ© pour une utilisation en production , car cela peut entraĂźner des plantages sporadiques lors du lancement des applications.
  • -Xshare:off - (vous) changez de CDS / AppCDS; dĂ©sactive complĂštement l'utilisation des donnĂ©es partagĂ©es (y compris les archives intĂ©grĂ©es)
  • -Xshare:auto - le comportement par dĂ©faut de la JVM lorsqu'elle, en cas d'impossibilitĂ© d'allouer la plage de mĂ©moire requise, se rend discrĂštement et charge les classes comme d'habitude

Au moment de la rédaction de cet article, Oracle s'efforce simplement de résoudre ces problÚmes, mais aucun numéro de version n'a encore été attribué.


Ces options nous sont partiellement utiles plus tard, mais pour l'instant, regardons ...


Applications AppCDS


Il existe plusieurs façons d'utiliser AppCDS. ruine ta vie optimiser le travail des microservices. Leur complexité et leur profit potentiel varient considérablement. Il est donc important de décider immédiatement lequel sera discuté plus tard.


Le plus simple est de ne pas utiliser mĂȘme AppCDS, mais seulement CDS - c'est lorsque seules les classes de plate-forme entrent dans l'archive partagĂ©e (voir "Une brĂšve introduction Ă  AppCDS"). Nous supprimerons cette option immĂ©diatement, car lorsqu'elle est appliquĂ©e aux microservices sur Spring Boot, elle donne trop peu de profit. Cela peut ĂȘtre vu par la proportion du nombre de classes partagĂ©es dans leur distribution gĂ©nĂ©rale en utilisant l'exemple d'un vrai microservice (voir le segment vert):



Plus complexe, mais prometteur est l'utilisation d'AppCDS Ă  part entiĂšre, c'est-Ă -dire l'inclusion des classes de bibliothĂšque et d'application dans la mĂȘme archive. Il s'agit de toute une famille d'options dĂ©rivĂ©es de combinaisons du nombre d'applications participantes et du nombre d'instances. Voici les Ă©valuations subjectives de l'auteur sur les avantages et les difficultĂ©s de diverses applications d'AppCDS.


Non.Les applicationsInstancesBénéfice CPUBénéfice RAMDifficulté
1UnUn+±Faible
2UnQuelques-uns++++Faible
3Quelques-unsUn Ă  la fois++++ÉlevĂ©
4Quelques-unsQuelques-uns++++++ÉlevĂ©

Faites attention:


  • Dans l'application Ă  une application dans une instance (n ° 1), le gain de mĂ©moire peut s'avĂ©rer nul ou mĂȘme nĂ©gatif (surtout lors de la mesure sous Windows )
  • La crĂ©ation de l'archive partagĂ©e correcte nĂ©cessite des actions, dont la complexitĂ© ne dĂ©pend pas du nombre de copies que l'application sera ensuite lancĂ©e (comparer les paires d'options n ° 1-2 et n ° 3-4)
  • Dans le mĂȘme temps, le passage d'une instance Ă  plusieurs donne Ă©videmment une augmentation du bĂ©nĂ©fice pour les deux indicateurs, mais n'affecte pas la complexitĂ© de la prĂ©paration.

Dans cet article, nous n'atteindrons que l'option n ° 2 (à travers le n ° 1), car elle est assez simple pour une connaissance approfondie d'AppCDS et seulement pour elle sans astuces supplémentaires, nous pouvons utiliser les archives JEP-350 Dynamic CDS récemment publiées, que je veux ressentir en action.


Archives dynamiques du CDS


Les archives dynamiques CDS JEP-350 , l'une des principales innovations de Java 13, sont conçues pour simplifier l'utilisation d'AppCDS. Pour ressentir la simplification, vous devez d'abord comprendre la complexitĂ©. Permettez-moi de vous rappeler que l'algorithme d'application «propre» classique pour AppCDS se compose de 3 Ă©tapes: (1) obtenir une liste des classes partagĂ©es, (2) crĂ©er une archive Ă  partir de celles-ci, et (3) exĂ©cuter l'application avec l'archive connectĂ©e. De ces Ă©tapes, seule la 3Ăšme est rĂ©ellement utile, le reste ne fait que la prĂ©parer. Et bien que l'obtention d'une liste de classes (Ă©tape # 1) puisse sembler trĂšs simple (dans certains cas, ce n'est mĂȘme pas nĂ©cessaire), en fait lorsque vous travaillez avec des applications non triviales, cela s'avĂšre ĂȘtre le plus difficile, en particulier en ce qui concerne Spring Boot. Le JEP-350 est donc nĂ©cessaire juste pour Ă©liminer cette Ă©tape, ou plutĂŽt l'automatiser. L'idĂ©e est que la JVM elle-mĂȘme dresse une liste des classes dont l'application a besoin, puis forme elle-mĂȘme l'archive dite «dynamique». D'accord, ça sonne bien. Mais le hic, c'est que maintenant il devient difficile de savoir Ă  quel moment cesser d'accumuler des classes et procĂ©der Ă  leur placement dans l'archive. Auparavant, dans l'AppCDS classique, nous avions choisi un tel moment nous-mĂȘmes et pouvions mĂȘme coincer entre ces actions pour changer quelque chose dans la liste des classes avant de le transformer en archive. Maintenant, cela se produit automatiquement et seulement Ă  un moment, pour lequel les ingĂ©nieurs JVM ont peut-ĂȘtre choisi la seule option de compromis - l'arrĂȘt rĂ©gulier de la JVM. Cela signifie que l'archive ne sera pas crĂ©Ă©e avant l'arrĂȘt de l'application. Cette solution a deux consĂ©quences importantes:


  • En cas de plantage de la JVM, l'archive ne sera pas crĂ©Ă©e, quelle que soit la beautĂ© de la liste des classes accumulĂ©es d'ici lĂ  (vous ne pouvez pas l'extraire plus tard en utilisant des moyens rĂ©guliers).
  • L'archive sera crĂ©Ă©e uniquement Ă  partir des classes qui ont rĂ©ussi Ă  se charger pendant la session d'application. Pour les applications Web, cela signifie que la crĂ©ation d'une archive en dĂ©marrant et en s'arrĂȘtant lĂ  n'est pas correcte, car alors de nombreuses classes importantes n'entreront pas dans l'archive. Il est nĂ©cessaire d'exĂ©cuter au moins une requĂȘte HTTP sur l'application (et il est prĂ©fĂ©rable de l'exĂ©cuter correctement dans tous les scĂ©narios) afin que toutes les classes qu'elle utilise rĂ©ellement soient chargĂ©es.

Une diffĂ©rence importante entre les archives dynamiques et statiques est qu'elles constituent toujours un «module complĂ©mentaire» sur les archives statiques de base, qui peuvent ĂȘtre soit des archives intĂ©grĂ©es dans le kit de distribution Java, soit crĂ©Ă©es sĂ©parĂ©ment de maniĂšre classique en 3 Ă©tapes.


Syntaxiquement, l'utilisation des archives Dynamic CDS se résume à deux lancements JVM avec deux options:


  1. Essai avec l'option -XX:ArchiveClassesAtExit=archive.jsa , à la fin de laquelle une archive dynamique sera créée (vous pouvez spécifier n'importe quel chemin et nom)
  2. Lancement utile avec l'option -XX:SharedArchiveFile=archive.jsa , qui utilisera l'archive précédemment créée

La deuxiÚme option n'est pas différente de la connexion d'une archive statique réguliÚre. Mais si tout à coup l'archive statique de base n'est pas à l'emplacement par défaut (à l'intérieur du JDK), cette option peut également inclure une indication du chemin d'accÚs, par exemple:


 -XX:SharedArchiveFile=base.jsa:dynamic.jsa 

(sous Windows, le sĂ©parateur de chemin doit ĂȘtre le caractĂšre ";")


Vous en savez maintenant assez sur AppCDS pour pouvoir le regarder en action.


Pratique


Lapin expérimental


Pour que notre application d'AppCDS dans la pratique ne se limite pas Ă  un HelloWorld typique, nous prendrons comme base la vraie application sur Spring Boot. Mes collĂšgues et moi devons souvent regarder les journaux des applications sur des serveurs de test distants et regarder en direct, tout comme ils sont Ă©crits. Utiliser pour cela un agrĂ©gateur de journaux Ă  part entiĂšre (comme ELK) n'est souvent pas appropriĂ©; tĂ©lĂ©charger des fichiers journaux Ă  l'infini - pendant longtemps, et regarder la sortie de la console grise de tail est dĂ©primant. Par consĂ©quent, j'ai crĂ©Ă© une application Web qui peut sortir tous les journaux en temps rĂ©el directement vers le navigateur, coloriser les lignes par niveau d'importance (en formatant XML en mĂȘme temps), agrĂ©ger plusieurs journaux en un seul, ainsi que d'autres astuces. Il s'appelle ANALOG (comme un «analyseur de journaux», bien que ce ne soit pas vrai) et se trouve sur GitHub . Cliquez sur la capture d'Ă©cran pour agrandir:



Techniquement, il s'agit d'une application sur Spring Boot + Spring Integration, sous le capot de laquelle tail , docker et kubectl (pour prendre en charge les journaux de fichiers, les conteneurs Docker et les ressources Kubernetes, respectivement). Il se prĂ©sente sous la forme du fichier JAR Spring Boot «épais» classique. Au moment de l'exĂ©cution, classes10K classes sont suspendues dans la mĂ©moire de l'application, dont la grande majoritĂ© sont des classes Spring et JDK. Évidemment, ces classes changent assez rarement, ce qui signifie qu'elles peuvent ĂȘtre placĂ©es dans une archive partagĂ©e et rĂ©utilisĂ©es dans toutes les instances de l'application, Ă©conomisant de la mĂ©moire et du CPU.


Expérience unique


Appliquons maintenant les connaissances existantes de Dynamic AppCDS au lapin expérimental. Puisque tout est connu en comparaison, nous aurons besoin d'un point de référence - l'état du programme avec lequel nous comparerons les résultats obtenus pendant l'expérience.


Remarques introductives


  • Toutes les autres commandes sont pour Linux. Les diffĂ©rences pour Windows et macOS ne sont pas fondamentales.
  • La compilation JIT peut sensiblement affecter les rĂ©sultats et, en thĂ©orie, pour la puretĂ© de l'expĂ©rience, elle pourrait ĂȘtre dĂ©sactivĂ©e (avec l'option -Xint , comme cela a Ă©tĂ© fait dans l' article mentionnĂ©), mais dans un souci de plausibilitĂ© maximale, il a Ă©tĂ© dĂ©cidĂ© de ne pas le faire.
  • Les nombres suivants concernant l'heure de dĂ©but ont Ă©tĂ© obtenus sur un serveur de test rapide. Sur les machines qui fonctionnent, des nombres similaires sont gĂ©nĂ©ralement plus modestes, mais comme nous ne nous intĂ©ressons pas aux valeurs absolues, mais aux incrĂ©ments de pourcentage, nous considĂ©rons cette diffĂ©rence comme insignifiante.
  • Afin de ne pas entrer prĂ©maturĂ©ment dans la complexitĂ© de la mesure de la mĂ©moire partagĂ©e, pour l'instant nous omettons d'obtenir des lectures prĂ©cises en octets. Au lieu de cela, nous introduisons le concept de « potentiel CDS », exprimĂ© en pourcentage du nombre de classes partagĂ©es par rapport au nombre total de classes chargĂ©es. Il s'agit bien sĂ»r d'une quantitĂ© abstraite, mais d'un autre cĂŽtĂ©, elle affecte directement la consommation rĂ©elle de mĂ©moire; de plus, sa dĂ©finition ne dĂ©pend pas du tout de l'OS, et pour son calcul, seuls les logs sont suffisants.

Point de référence


Soit ce point l'état d'une application fraßchement téléchargée, c'est-à-dire sans utilisation explicite d'aucun AppCDS'ov et autres. Pour l'évaluer, nous avons besoin de:


  1. Installez OpenJDK 13 (par exemple, la distribution Liberica domestique, mais pas la version lite).
    Il doit Ă©galement ĂȘtre ajoutĂ© Ă  la variable d'environnement PATH ou Ă  JAVA_HOME , par exemple, comme ceci:


     export JAVA_HOME=~/tools/jdk-13 

  2. Téléchargez ANALOG (au moment de la rédaction, la derniÚre version était la v0.12.1).


    Si nécessaire, vous pouvez spécifier dans le fichier config/application.yaml du paramÚtre server.address le nom d'hÎte externe pour accéder à l'application (par défaut, localhost est spécifié).


  3. Activez la journalisation du chargement de classe JVM.
    Pour ce faire, vous pouvez JAVA_OPTS la variable d'environnement JAVA_OPTS avec cette valeur:


     export JAVA_OPTS=-Xlog:class+load=info:file=log/class-load.log 

    Cette option sera transmise Ă  la JVM et lui demandera de garantir la source de chaque classe.


  4. Exécutez un test:


    1. Exécutez l'application avec le script bin/analog
    2. Ouvrez http: // localhost: 8083 dans le navigateur, piquez les boutons et les daws
    3. ArrĂȘtez l'application en appuyant sur Ctrl+C dans la console de script bin/analog

  5. Prendre le résultat (à partir des fichiers du log/ répertoire)


    • Nombre total de classes chargĂ©es (par class-load.log ):


       cat class-load.log | wc -l 10463 

    • Combien d'entre eux sont tĂ©lĂ©chargĂ©s Ă  partir d'une archive partagĂ©e (selon elle):


       grep -o 'source: shared' - class-load.log 1146 

    • Heure de dĂ©but moyenne (aprĂšs une sĂ©rie de dĂ©marrages; par analog.log ):


       grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.5225 



Ainsi, Ă  cette Ă©tape, le potentiel du CDS Ă©tait de 1146/10463=0,1095 -11% . Si vous ĂȘtes surpris de l'origine des classes partagĂ©es (aprĂšs tout, nous n'avons pas encore inclus d'AppCDS), je vous rappelle qu'Ă  partir de la 12Ăšme version, le JDK inclut l' archive CDS finie $JAVA_HOME/lib/server/classes.jsa , construite par pas moins de liste de classes prĂȘte:


 cat $JAVA_HOME/lib/classlist | wc -l 1170 

Maintenant, aprÚs avoir évalué l'état initial de l'application, nous pouvons lui appliquer AppCDS et, par comparaison, comprendre ce que cela donne.


Expérience de base


Comme la documentation nous a Ă©tĂ© lĂ©guĂ©e, pour crĂ©er une archive AppCDS dynamique, vous devez effectuer un seul essai de l'application avec l'option -XX:ArchiveClassesAtExit . DĂšs le prochain lancement, l'archive peut ĂȘtre utilisĂ©e et recevoir des bĂ©nĂ©fices. Pour vĂ©rifier cela sur le mĂȘme lapin expĂ©rimental (AnaLog), vous avez besoin de:


  1. Ajoutez l'option spécifiée à la commande d'exécution:


     export JAVA_OPTS="$JAVA_OPTS -XX:ArchiveClassesAtExit=work/classes.jsa" 

  2. Étendre la journalisation:


     export JAVA_OPTS="$JAVA_OPTS -Xlog:cds=debug:file=log/cds.log" 

    Cette option forcera le processus de construction d'une archive CDS Ă  ĂȘtre enregistrĂ© lorsque l'application sera arrĂȘtĂ©e.


  3. Effectuez le mĂȘme test que pour le point de rĂ©fĂ©rence:


    1. Exécutez l'application avec le script bin/analog
    2. Ouvrez http: // localhost: 8083 dans le navigateur, piquez les boutons et les daws
    3. ArrĂȘtez l'application en appuyant sur Ctrl+C dans la console de script bin/analog
      AprĂšs cela, un Ă©norme footcloth avec toutes sortes d'avertissements devrait tomber dans la console, et le log/cds.log devrait ĂȘtre rempli de dĂ©tails; ils ne nous intĂ©ressent pas encore.

  4. Faites passer le mode de lancement d'essai Ă  utile:


     export JAVA_OPTS="-XX:SharedArchiveFile=work/classes.jsa -Xlog:class+load=info:file=log/class-load.log -Xlog:class+path=debug:file=log/class-path.log" 

    Ici, nous ne JAVA_OPTS pas la variable JAVA_OPTS , mais nous la JAVA_OPTS par de nouvelles valeurs qui incluent (1) l'utilisation d'une archive partagée, (2) la journalisation des sources de classe et (3) la journalisation des vérifications de chemin de classe.


  5. Effectuez un lancement utile de l'application selon le schéma du paragraphe 3.


  6. Prendre le résultat (à partir des fichiers du log/ répertoire)


    • VĂ©rification de l'application rĂ©elle d'AppCDS (par le class-path.log ):


       [0.011s][info][class,path] type=BOOT [0.011s][info][class,path] Expecting BOOT path=/home/upc/tools/jdk-13/lib/modules [0.011s][info][class,path] ok [0.011s][info][class,path] type=APP [0.011s][info][class,path] Expecting -Djava.class.path=/home/upc/tmp/analog/lib/analog.jar [0.011s][info][class,path] ok 

      Les marques ok aprÚs les lignes type=BOOT et type=APP indiquent respectivement l'ouverture, la vérification et le chargement réussis des archives CDS intégrées et appliquées.


    • Nombre total de classes chargĂ©es (par class-load.log ):


       cat class-load.log | wc -l 10403 

    • Combien d'entre eux sont tĂ©lĂ©chargĂ©s Ă  partir d'une archive partagĂ©e (selon elle):


       grep -o 'source: shared' -c class-load.log 6910 

    • Heure de dĂ©but moyenne (aprĂšs une sĂ©rie de dĂ©marrages; par fichier analog.log ):


       grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.04167 



Mais Ă  ce 6910/10403≈0,66 , le potentiel du CDS Ă©tait dĂ©jĂ  de 6910/10403≈0,66 = 66% , c'est-Ă -dire qu'il a augmentĂ© de 55% par rapport au point de rĂ©fĂ©rence. Dans le mĂȘme temps, le temps de lancement moyen a Ă©tĂ© rĂ©duit de (4,5225-4,04167)=0,48 seconde, soit le dĂ©marrage est plus rapide de ≈10,6% de la valeur initiale.


Analyse des résultats


Le titre de travail de l'article est: "Pourquoi si peu?"


Nous, comme, avons tout fait selon les instructions, mais toutes les classes n'étaient pas dans l'archive. Leur nombre affecte le temps de lancement non moins que la puissance de calcul de la machine de l'expérimentateur, nous allons donc nous concentrer sur ce nombre.


Si vous vous en souvenez, nous avons ignorĂ© le fichier log/cds.log crĂ©Ă© lors de l'arrĂȘt de l'application expĂ©rimentale aprĂšs la pĂ©riode d'essai. Dans ce fichier HotSpot, la JVM a gentiment notĂ© les classes d'avertissement pour chaque classe qui n'apparaissait pas dans l'archive CDS. Voici le nombre total de ces marques:


 grep -o '[warning]' cds.log -c 3591 

Étant donnĂ© que seules 10 000 classes et plus sont mentionnĂ©es dans le journal class-load.log et que 66% d'entre elles sont tĂ©lĂ©chargĂ©es Ă  partir de l'archive, il n'est pas difficile de comprendre que les 3600 classes rĂ©pertoriĂ©es dans cds.log reprĂ©sentent les 44% «manquants» du potentiel CDS. Vous devez maintenant dĂ©couvrir pourquoi ils ont Ă©tĂ© ignorĂ©s.


Si vous regardez le journal cds.log, il s'avĂšre qu'il n'y a que 4 raisons uniques pour sauter des classes. Voici des exemples de chacun d'eux:


 Skipping org/springframework/web/client/HttpClientErrorException: Not linked Pre JDK 6 class not supported by CDS: 49.0 org/jrobin/core/RrdUpdater Skipping java/util/stream/Collectors$$Lambda$554: Unsafe anonymous class Skipping ch/qos/logback/classic/LoggerContext: interface org/slf4j/ILoggerFactory is excluded 

Parmi les 3591 classes manquées, ces raisons se retrouvent ici avec une telle fréquence:



Examinez-les de plus prĂšs:


  • Unsafe anonymous class
    JVM “” , -, .


  • Not linked
    , “” , , . , StackOverflow . , , “” () JAR- , AppCDS. , ( ).


  • Pre JDK 6 class
    , CDS Java 5. class- , CDS . , , 6, Java, . - , runtime- (, slf4j).


  • Skipping ... : super class/interface ... is excluded
    , “” . CDS', . Par exemple:


     [warning][cds] Pre JDK 6 class not supported by CDS: 49.0 org/slf4j/spi/MDCAdapter [warning][cds] Skipping ch/qos/logback/classic/util/LogbackMDCAdapter: interface org/slf4j/spi/MDCAdapter is excluded 


Conclusion


CDS 100%.

, , , , , . .



JEP-310 , AppCDS JDK. . , . CDS (, , ) .


Afin de cloner le lapin expĂ©rimental (exĂ©cuter AnaLog dans plusieurs cas), nous devons changer quelque chose dans les paramĂštres; cela permettra aux processus levĂ©s de ne pas «coude». GrĂące Ă  Spring Boot, vous pouvez le faire sans modifier ni copier aucun fichier; tous les paramĂštres peuvent ĂȘtre remplacĂ©s par les options JVM. Le transfert de ces options Ă  partir de la variable d'environnement ANALOG_OPTSfournit un script de lancement, gentiment gĂ©nĂ©rĂ© par Gradle.


 export ANALOG_OPTS="-Djavamelody.enabled=false -Dlogging.config=classpath:logging/logback-console.xml" export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091" 

JavaMelody, , , . TCP- ; .


, , JVM AppCDS . JAVA_OPTS JVM Unified Logging Framework :


 export JAVA_OPTS="-Xlog:class+load=info:file=log/class-load-%p.log -Xlog:class+path=debug:file=log/class-path-%p.log" export JAVA_OPTS="$JAVA_OPTS -XX:SharedArchiveFile=work/classes.jsa" 

%p , JVM (PID). AppCDS , ( ).



, . . :


  1. server.port nodes.this.agentPort , :


     export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091" 

    , ( ).


  2. bin/analog


    () http://localhost:8091 ,


  3. PID ( ), :


     pgrep -f analog 13792 

  4. pmap ( ):


     pmap -XX 13792 | sed -n -e '2p;$p' Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked ProtectionKey VmFlagsMapping 3186952 1548 1548 328132 325183 3256 0 10848 314028 212620 314024 0 0 0 0 0 0 0 325183 0 KB 

    ; .


  5. 1-4 (, ).




pmap . CDS' . , , PSS:


The "proportional set size" (PSS) of a process is the count of pages it has in memory, where each page is divided by the number of processes sharing it. So if a process has 1000 pages all to itself, and 1000 shared with one other process, its PSS will be 1500.

, , “ ” . , .


PSS , :


Iteration:12345
PSS of inst#1:339 088313 778305 517301 153298 604
PSS of inst#2:314 904306 567302 555299 919
PSS of inst#3:314 914311 008308 691
PSS of inst#4:306 563304 495
PSS of inst#5:294 686
Average:339 088314 341308 999305 320301 279

, - :


  • “”
  • , PSS
  • “” , PSS

, . AppCDS. , -XX:SharedArchiveFile=work/classes.jsa -Xshare:off , CDS . , .



:


  • PSS AppCDS CDS.
    . , , HelloWorld- JVM CDS 2 , CDS. PSS CDS, . :


  • PSS AppCDS 2- ; 3- .
    , , , . , AppCDS, , , 3- .
    : , CDS? :


  • CDS/AppCDS JVM , PSS . , , pmap , “” sed '. :


     pmap -X `pgrep -f analog` 14981: # ... Address Perm Offset Device Inode Size Rss Pss ... Mapping # ... ... 7faf5e31a000 r-xp 00000000 08:03 269427 17944 14200 14200 ... libjvm.so # ... ... 7faf5f7f9000 r-xp 00000000 08:03 1447189 1948 1756 25 ... libc-2.27.so 

    ( Mapping ) , “” . JVM ( libjvm.so ), ( libc-2.27.so ). :


    For the Java VM, the read-only parts of the loaded shared libraries (ie libjvm.so ) can be shared between all the VM instances running at the same time. This explains why, taking together, the two VM's consume less memory (ie have a smaller memory footprint) than the simple sum of their single resident set sizes when running alone.


. , , . , , JVM , Java- . GeekOut:



, , , AppCDS , .. Java-. , JVM, , - .


VisualVM Metaspace AppCDS , :


AppCDS



AppCDS



, 128 Metaspace AppCDS 64.2 MiB / 8.96 MiB ≈7,2 , CDS . (. ) 66.4 MiB / 13.9 MiB ≈4,8 . , AppCDS , Metaspace. Metaspace, , CDS .


Au lieu d'une conclusion


Spring Boot AppCDS – JVM, .


  • JEP-350 Dynamic CDS Archives – JDK 13.
  • Spring Boot Ăł CDS ( ). , 100% - 66% . , ≈11% ( 15%, ).
  • , 5- PSS ( ). , AppCDS , , 8% (PSS). , CDS, , . AppCDS .
  • Metaspace, , AppCDS 5 , CDS.

, , AppCDS, , “killer feature”. Spring Boot. , , AppCDS . , , AppCDS Spring Boot. , 



by Nick Fewings on Unsplash

Source: https://habr.com/ru/post/fr472638/


All Articles