I. Introduction

Suite à mon article http://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 à http://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 écrite 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.

Image non disponible

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 un icône qui est ajouté qui permet de lancer le monitoring avec un certain nombre de paramètres.


Par exemple pour Eclipse

Image non disponible

Image non disponible


Et pour Netbeans

Image non disponible

  • L'autre solution est de lancer son application avec l'option agentpath
 
Sélectionnez

java.exe -agentpath:yjpagent.dll ClassATester



Pour les applications déployée 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.

Image non disponible Image non disponible

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

Image non disponible


Lorsqu'on clique sur l'application, un onglet est crée.

Image non disponible


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

Image non disponible Image non disponible

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

Image non disponible


Pour avoir plus d'informations, il faut faire un dump

Image non disponible


Avec ce dump, on peut :

1. Avoir des statistiques sur l'utilisation du CPU

Image non disponible


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

Image non disponible


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

Image non disponible

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"

Image non disponible


On peut voir que dans ce cas là, c'est le fonction catalogBean.findCategory qui consomme le plus.



Prenons au autre exemple plus complexe. Par exemple l'émulateur JavaCPC d'ordinateur Amstrad CPC

Plus d'informations sur http://sourceforge.net/projects/javacpc/

Image non disponible


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 prenne 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.

Image non disponible


De plus l'onglet Deadlocks se remplira automatiquement avec les informations nécessaires s'il y a des verrous mortels (dead lock)

Image non disponible


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.

Image non disponible


Puis nous avons les informations nécessaires à la résolution des problèmes (mauvaise configuration du pool de thread, blocs synchronisés, ...).

Image non disponible

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.

Image non disponible


Puis une notification va apparaitre.

Image non disponible


Et enfin on pourra obtenir les informations.

Image non disponible

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)

Image non disponible


Faire dump CPU.

Image non disponible



Puis regarder l'ensemble des requêtes exécutées.

Image non disponible

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

Voici un exemple avec l'application YAPS.

Image non disponible


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.

Image non disponible
Image non disponible

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".

Image non disponible

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écution de Major GC et de Minor GC) comme on peut le voir sur l'écran suivant.

Image non disponible


A tout moment il est possible de forcer le GC.

Image non disponible



Comme pour tous les autres modules de YourKit, pour avoir plus d'informations il faut passer par un dump.

Image non disponible


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.

Image non disponible


Puis on fait un dump pour avoir des informations plus détaillées.

Image non disponible Image non disponible


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.

Image non disponible

Explorer des objets (Object explorer).

Image non disponible

Des informations sur les objets regroupés par Class loaders.

Image non disponible


La fonctionnalité "Inspection" permet de trouver un certain nombre d'utilisations "bizarres" de la mémoire.

Image non disponible


Une autre fonctionnalité intéressante que l'on traitera plus tard est "Object generations"

A 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

A 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.

Image non disponible


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.

Image non disponible

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.

Image non disponible

Puis on pourra afficher un certain nombre de chemins.

Image non disponible


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.

Image non disponible


Et on obtient la liste des objets avec la variation de taille.

Image non disponible
Image non disponible


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érer comme l'age 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
Image non disponible
  • Soit lors d'une demande de dump
Image non disponible

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 ancien auront l'age 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.

Image non disponible

Ces problèmes sont regroupés en trois catégories :

Eventuels gaspillages de mémoire Duplicate Strings : Permet de detecter 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 detecter les tableaux qui ont un grand nombre d'élément null.
Zero Length Arrays : Permet de detecter les tableaux vides.
Eventuelles fuites Objects Retained by Inner Classes : Permet de detecter des objets vivants à cause d'un lien avec leur class interne.
Lost SWT Controls : Permet de detecter des éléments SWT non accéssible par l'IHM.
Autres Highly Referenced Objects : Permet de detecter les objet avec un couplage fort.
Self Referencing Objects : Permet de detecter les objets qui se référence eux même à l'aide de this.
Non-Serializable Objects Referenced from Serializable Objects : Permet de detecter les objets non serializable accessible pas des objets serializable

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 pour 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 http://www.developpez.net/forums/f413/java/edi-outils-java/tests-performance/