Audit de performance avec YourKit Java Profiler

Antonio Gomes Rodrigues

Introduction à YourKit Java Profiler

Commentez ?

- Titre : Audit de performance avec YourKit Java Profiler

- Auteur : Antonio Gomes Rodrigues

- Parution : 21 décembre 2009

- Licence : Copyright ® 2009 Gomes Rodrigues. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Gestion de la mémoire par la JVM

- III. Découverte de YourKit Java Profiler

- III-A. Fonctionnement de YourKit Java Profiler

- III-A-1. Exécution de l'application cible

- III-A-2. Profilage de l'application cible

- IV. Analyse des performances

- IV-A. Consommation CPU

- IV-A-1. Détecter les goulots d'étranglement

- IV-B. Problèmes de threads

- IV-B-1. Détecter les deadlocks

- IV-C. Optimisation des requêtes SQL

- IV-C-1. Détecter les requêtes SQL consommatrices

- IV-D. Optimisation des JSP

- IV-E. Exceptions

- V. Analyse de la mémoire

- V-A. Garbage Collector

- V-B. Consommation mémoire

- V-B-1. Détecter les objets consommateurs de mémoire

- V-B-2. Détecter les fuites de mémoire

- V-B-2-a. Comparaison de dump

- V-B-2-b. Générations

- V-B-3. Inspections

- VI. Conclusion

- VII. Remerciements

- VIII. Références

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

La détection des deadlocks est assez simple.
Il suffit de monitorer l'application et au bout d'un moment, les threads vont se bloquer.

Puis une notification va apparaitre.

Et enfin on pourra obtenir les informations.

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/