I. Introduction▲
Suite à mon article https://arodrigues.developpez.com/tutoriels/java/performance/audit-performances-application-java-ee/, nous allons approfondir certains points à l'aide de l'outil YourKit Java Profiler.
Ces points seront :
- Profiler la consommation du CPU,
- Profiler la consommation de la mémoire,
- Profiler les requêtes SQL,
- Profiler l'exécution des threads.
Mais avant cela, regardons d'un peu plus près la gestion de la mémoire par la JVM.
II. Gestion de la mémoire par la JVM▲
Il est nécessaire de bien comprendre comment la JVM gère la mémoire avant d'aller plus loin.
Pour cela je vous renvoie à https://arodrigues.developpez.com/tutoriels/java/performance/audit-performances-application-java-ee/#LIV-B-1
III. Découverte de YourKit Java Profiler▲
YourKit Java Profiler est comme son nom l'indique un profiler pour les applications écrites en Java (une version existe aussi pour .Net).
Il est édité par la société YourKit, LLC qui existe depuis 2003.
Pour les prix de la licence, cela va de 389€ (Une licence valable un an avec un support basique) à 2009€ (Une licence flottante avec un an de support avancé).
Notons qu'il existe une version moins chère pour les écoles (academic) et même une version gratuite pour les projets open source.
Les fonctionnalités sont exactement les mêmes d'une version à l'autre, seule la possibilité de l'utiliser sur une application commerciale est interdite pour la version academic et open source (version pour les développeurs d'un projet open source).
Plus d'informations sur les prix sur http://www.yourkit.com/purchase/index.jsp
Ces atouts sont :
Une grande richesse fonctionnelle |
Profilage de mémoire, CPU, thread, exceptions, JDBC ... Metrics sur le Ramasse miettes (Garbage Collector), Comparaison de snapshot/dump ... |
Multiplate-forme | Windows, Linux, Solaris SPARC/Intel, Mac OS X et FreeBSD |
Intégration aux principaux IDE du marché | Eclipse, NetBeans, IntelliJ IDEA... |
Intégration aux principaux serveurs d'application | WebSphere, WebLogic, Tomcat, JBoss, GlassFish... |
Contrôler l'overhead pour s'adapter à la machine cible | Modification/activation des paramètres de monitoring |
Ouverture à la communauté open source | Une licence gratuite pour les projets open source |
III-A. Fonctionnement de YourKit Java Profiler▲
YourKit Java Profiler se présente sous la forme d'une fenêtre.
C'est sur cet écran que l'on va pouvoir :
- Monitorer l'application cible,
- Installer l'intégration aux IDE et aux serveurs d'application,
- Accéder à la documentation de l'outil,
- Ouvrir des dumps.
Passons à l'exécution de l'application cible qui va être monitorée.
III-A-1. Exécution de l'application cible ▲
Il y a deux façons de lancer l'application qui va être profilée :
- par l'IDE,
- par un paramètre d'exécution de son application ou de son serveur d'application.
- Avec votre IDE, on a une icône qui est ajoutée qui permet de lancer le monitoring avec un certain nombre de paramètres.
Par exemple pour Eclipse
Et pour Netbeans
- L'autre solution est de lancer son application avec l'option agentpath
java.exe -agentpath:yjpagent.dll ClassATester
Pour les applications déployées sur un serveur d'application, il faut utiliser l'assistant d'intégration aux serveurs d'application de YourKit qui créera un fichier "batch" de lancement de votre serveur d'application.
En fonction des options choisies, le surplus de charge (overhead) sera plus ou moins grand.
III-A-2. Profilage de l'application cible▲
Une fois l'application exécutée, on la voit apparaitre sur le tableau de bord de YourKit
Lorsqu'on clique sur l'application, un onglet est créé.
Comme on peut le voir sur la capture d'écran, on peut suivre un certain nombre de metrics.
Pour avoir plus de détails, il faut faire un dump
IV. Analyse des performances▲
IV-A. Consommation CPU▲
Maintenant que l'application est en cours d'exécution et monitorée, on peut analyser le temps passé dans chaque portion de code pour découvrir les goulots d'étranglement.
Comme on peut le voir sur la capture d'écran suivante, on peut avoir quelques informations en direct
Pour avoir plus d'informations, il faut faire un dump
Avec ce dump, on peut :
1. Avoir des statistiques sur l'utilisation du CPU
Cela permet de connaitre les portions de code les plus consommatrices de temps CPU.
2. Avoir des statistiques sur certains aspects Java EE (requêtes SQL, Servlet, JSP et JNDI)
3. Retrouver les informations qu'on a en direct
Une fois que la portion de code suspecte a été identifiée, on peut la regarder d'un peu plus près à l'aide de son IDE
IV-A-1. Détecter les goulots d'étranglement▲
Nous allons monitorer l'application exemple YAPS qui vient avec le livre "Le cahier des programmeurs - Java EE 5".
Vous trouverez plus d'informations sur http://www.antoniogoncalves.org/xwiki/bin/view/Book/JavaEE5Fr
Une fois le dump réalisé (voir les étapes précédentes), on peut voir les portions de code les plus consommatrices à l'aide de la fonction "Hot spots"
On peut voir que dans ce cas là, c'est la fonction catalogBean.findCategory qui consomme le plus.
Prenons un autre exemple plus complexe. Par exemple l'émulateur JavaCPC d'ordinateur Amstrad CPC
Plus d'informations sur http://sourceforge.net/projects/javacpc/
Comme on peut le voir sur la capture d'écran, c'est l'émulation du CPU (Z80) suivi du chip graphique (Gate Array) et du chip sonore (AY-3-8912) qui prend le plus de ressource.
En regardant de plus près dans le code, on verra que le problème vient du fait que le développeur utilise d'énormes instructions "switch case" pour émuler les instructions CPU (Z80).
IV-B. Problèmes de threads▲
C'est dans le module Thread que l'on va pouvoir scruter l'activité des threads.
De plus l'onglet Deadlocks se remplira automatiquement avec les informations nécessaires s'il y a des verrous mortels (dead lock)
Un dump donnera encore une fois plus de détails sur les threads.
Avant de créer le dump il faut activer le recueil d'informations supplémentaires sur les threads.
Puis nous avons les informations nécessaires à la résolution des problèmes (mauvaise configuration du pool de thread, blocs synchronisés...).
IV-B-1. Détecter les deadlocks▲
IV-C. Optimisation des requêtes SQL▲
Afin de capturer les requêtes SQL exécutées, activez le monitoring J2EE (case Profile J2EE dans les options de profiling)
Faire dump CPU.
Puis regarder l'ensemble des requêtes exécutées.
IV-C-1. Détecter les requêtes SQL consommatrices▲
Voici un exemple avec l'application YAPS.
Une fois la requête fautive détectée, approfondissez pour trouver la source du problème afin de le corriger (ajout d'index, réécriture de la requête, changement du niveau d'isolation...).
IV-D. Optimisation des JSP▲
On fait la même chose que pour la recherche des requêtes SQL consommatrices.
IV-E. Exceptions▲
La mauvaise utilisation des exceptions pouvant poser des problèmes de performance, il peut être utile de regarder si le nombre et le type d'exception levé par l'application correspondent à ce qui est attendu.
Pour plus de commodité, on peut regrouper les exceptions par type.
Une fois l'exception sélectionnée, on peut voir sa "stacktrace".
V. Analyse de la mémoire▲
V-A. Garbage Collector▲
Afin de vérifier le comportement du Garbage Colector (Ramasse Miettes), YourKit Java Profiler comprend un certain nombre de metrics (temps passé dans le GC, nombre d'exécutions de Major GC et de Minor GC) comme on peut le voir sur l'écran suivant.
À tout moment il est possible de forcer le GC.
Comme pour tous les autres modules de YourKit, pour avoir plus d'informations il faut passer par un dump.
Bien sûr, on peut classer le résultat avec différents critères afin de trouver les problèmes.
Ce module nous permet de :
- Affiner le paramétrage de la JVM,
- Détecter la création excessive d'objets,
- ...
V-B. Consommation mémoire▲
Regardons d'un peu plus près la consommation mémoire de notre application monitorée.
De la même façon que pour la consommation CPU, on a des informations en direct.
Puis on fait un dump pour avoir des informations plus détaillées.
Sur cet écran, on peut avoir plusieurs informations utiles comme la liste des objets les plus consommateurs en mémoire (Biggest objects) et leurs chemins vers GC Roots.
Explorer des objets (Object explorer).
Des informations sur les objets regroupés par Class loaders.
La fonctionnalité "Inspection" permet de trouver un certain nombre d'utilisations "bizarres" de la mémoire.
Une autre fonctionnalité intéressante que l'on traitera plus tard est "Object generations"
À tout moment on peut utiliser des expressions régulières pour n'afficher que les classes de notre projet.
Regardons maintenant comment exploiter toutes ces fonctionnalités.
V-B-1. Détecter les objets consommateurs de mémoire▲
À l'image de la fonction "Hot spots" pour la consommation CPU, il existe la fonction "Biggest objects" afin de détecter les objets les plus consommateurs de mémoire.
Sur JavaCPC, on remarque qu'il y a l'utilisation de deux gros tableaux d'entiers pour la partie enregistrement/lecture du son sortant du chip sonore.
V-B-2. Détecter les fuites de mémoire▲
Une utilisation intéressante d'un profiler est la détection des fuites de mémoire. Pour cela, YourKit Java Profiler nous met à disposition un certain nombre de fonctionnalités. Mais avant de les étudier, regardons de plus près une méthode de détection des fuites de mémoire.
Le principe pour cette détection est de trouver les objets non utilisés qui restent en mémoire et de trouver la raison de leur présence.
Malheureusement le nombre d'objets en mémoire à surveiller peut être conséquent. Pour limiter le nombre d'objets à surveiller il existe plusieurs solutions :
- On lance un test de charge et on regarde les objets qui ne font que grossir,
- On teste fonctionnalité par fonctionnalité et on vérifie que les objets sont bien détruits,
- On étudie un dump mémoire pour détecter les objets les plus gros et/ou nombreux,
- On fait une comparaison de dumps de mémoire,
- On utilise la fonctionnalité Generations.
Une fois ces objets détectés, leurs chemins vers GC Roots permettra de savoir pourquoi ils n'ont pas été détruits par le GC.
Puis on pourra afficher un certain nombre de chemins.
Regardons d'un peu plus près la phase de détection des objets fautifs.
V-B-2-a. Comparaison de dump▲
On peut utiliser la fonction de comparaison de dumps (avant lancement du traitement et après le traitement) afin de visualiser les objets qui restent en mémoire après traitement.
Pour cela il faut ouvrir les deux dumps puis utiliser la fonction de comparaison.
Et on obtient la liste des objets avec la variation de taille.
Une fois ces objets détectés, leurs chemins vers GC Roots permettra de savoir pourquoi ils n'ont pas été détruits par le GC.
V-B-2-b. Générations▲
Le principe des générations est de numéroter/tagger chaque objet lors de sa création (ce numéro peut être considéré comme l'âge de l'objet). Ce numéro correspond à un numéro incrémenté automatiquement.
Il existe deux façons d'incrémenter ce numéro :
- Soit par demande explicite avec ce bouton
- Soit lors d'une demande de dump
Donc lors du lancement de l'application, tous les objets créés ont comme âge 1.
Puis on incrémente ce numéro (dump ou avec le bouton).
Les prochains objets qui seront créés auront pour âge 2.
Et ainsi de suite.
De cela on peut en conclure que les objets les plus anciens auront l'âge le plus bas.
Cela permet de repérer facilement les objets qui sont en mémoire depuis longtemps afin de les étudier de plus près.
De plus, en incrémentant au bon moment, on peut détecter les objets qui restent en mémoire malgré qu'ils ne soient plus utilisés.
Par exemple, si après l'utilisation d'une fonctionnalité de notre application, on incrémente ce numéro, il ne devrait pas avoir d'objets dépendants de cette fonctionnalité avec ce nouveau numéro.
V-B-3. Inspections▲
Cette fonctionnalité permet de détecter d'éventuels problèmes.
Ces problèmes sont regroupés en trois catégories :
Éventuels gaspillages de mémoire |
Duplicate Strings : Permet de détecter les chaines de caractères dupliquées. Null Fields : Permet de trouver les instances de variables avec un fort pourcentage de valeur à null. Sparse Arrays : Permet de détecter les tableaux qui ont un grand nombre d'élément null. Zero Length Arrays : Permet de détecter les tableaux vides. |
Éventuelles fuites |
Objects Retained by Inner Classes : Permet de détecter des objets vivants à cause d'un lien avec leur class interne. Lost SWT Controls : Permet de détecter des éléments SWT non accessibles par l'IHM. |
Autres |
Highly Referenced Objects : Permet de détecter les objets avec un couplage fort. Self Referencing Objects : Permet de détecter les objets qui se référence eux-mêmes à l'aide de this. Non-Serializable Objects Referenced from Serializable Objects : Permet de détecter les objets non serializables accessibles par des objets serializables |
VI. Conclusion▲
Yourkit Java profiler est d'une grande aide lors d'un audit de performance par ses nombreuses fonctionnalités et sa simplicité d'utilisation.
De plus le suivi est très bon (plusieurs mises à jour sont apparues lors de l'écriture de cet article).
VII. Remerciements▲
Je remercie la société YourKit de m'avoir fourni une licence d'évaluation plus longue afin de pouvoir écrire cet article.
Merci à baptiste wicht pour sa relecture technique.
Merci à Furr pour sa relecture orthographique.
VIII. Références▲
Une documentation assez complète peut se trouver sur le site de l'éditeur http://www.yourkit.com/overview/index.jsp
Le forum test et performance de developpez.com https://www.developpez.net/forums/f413/java/edi-outils-java/tests-performance/