FeaturesPluginsDocs & SupportCommunityPartners

Didacticiel pour le Profileur NetBeans

Cet article fait partie de la série d'articles soumis par la communauté NetBeans dans le cadre du concours "Win With NetBeans".

Le profileur NetBeans est un outil puissant qui fournit des informations importantes sur le comportement d'une application. Tout en n'introduisant que très peu de dégradation de performance, le profileur NetBeans permet de surveiller la performance d'utilisation du processeur et de la mémoire. Ce didacticiel vous démontre l'utilisation du profileur NetBeans pour:

Certaines portions de ce didacticiel sont tirées d'un article daté de juin 2004 dans le Java Developer's Journal sur la technologie JFluid qui constitue la technologie sous-jacente du profileur NetBeans. Ces portions sont reprises avec l'accord du Java Developer's Journal.

Ce didacticiel dure environ une heure.

Démarrage rapide avec l'EDI NetBeans 4 pour les applications Web
Si vous n'êtes pas familiers de l'EDI NetBeans 4, consultez Guide Rapide pour Applications Web avec l'EDI NetBeans 4.0. Une fois la lecture de ce document faite, ce didacticiel pourra parfaire vos compétences!

Mise en place de l'environnement

Installation du profileur

Le profileur NetBeans est encore en version bêta et disponible au travers d'un téléchargement séparé. Pour suivre ce didacticiel, il vous faut installer la version la plus récente du profileur NetBeans en suivant ces instructions d'installation.

Configuration de Tomcat

Pour profiler une application web, il est nécessaire d'utiliser le serveur utilisé avec la JVM fournie par le profileur. Aussi, pour que l'on puisse profiler une application s'exécutant dans Tomcat, il faut procéder à une légère modification du script de démarrage pour que la variable d'environnement JAVA_HOME soit correctement positionnée. De plus, il vous faut "compléter" la JVM fournie par le profileur en lui rajoutant des répertoires issus du JDK 1.4.2. Pour tous ces détails, il suffit de suivre les deux étapes dans les Instructions Tomcat qui permettent de se brancher et de profiler une application.

Obtenir les fichiers sources

Ce didacticiel utilise une application dérivée du projet HelloWeb créé dans la documentation Guide Rapide pour Applications Web avec EDI NetBeans 4.0. Les modifications apportées sont liées à l'ajout d'une servlet de gestion des requêtes qui crée un JavaBeans avant de passer la main à la JSP qui génère la réponse. La servlet est volontairement mal rédigée pour mettre en valeur les capacités du profileur NetBeans.

L'ensemble des fichiers se trouvent dans l'archive ProfilingTutorial.zip.

  1. Sur votre système de fichiers, créez un répertoire qui contient les fichiers décompressés de l'archive. A partir de maintenant nous y ferons référence sous le nom de $UNZIPHOME.

  2. Cliquez ici pour télécharger l'archive ProfilingTutorial.zip file.

  3. Utilisez un utilitaire qui décompresse les fichiers contenus dans l'archive ProfilingTutorial.zip vers le répertoire $UNZIPHOME.

    $UNZIPHOME possède maintenant des répertoires web and src. Le répertoire web contient les deux fichiers JSP et les descripteurs de déploiement. Le répertoire src contient les fichiers source de la servlet et des JavaBeans.

Création du projet

Création d'un projet web à partir des fichiers source fournis.

  1. Choisisez File > New Project (Maj-Ctrl-N). Sélectionnez la catégorie Web. Choisissez ensuite "Web Project with Existing Sources" et cliquez sur Next.

  2. Pour "Location", cliquez sur "Browse" pour sélectionner la racine de l'application web. Il s'agit ici de $UNZIPHOME, le répertoire dans lequel se trouve le contenu décompressé du fichier ProfilingTutorial.zip.

  3. Pour "Project Name", saisissez DidacticielProfileur et cliquez sur Next.

  4. Choisissez enfin "Finish". L'EDI crée alors le répertoire projet $UNZIPHOME/nbproject et le fichier $UNZIPHOME/build.xml. Le projet DidacticielTutorial est alors ouvert dans l'EDI. Vous pouvez voir sa présentation logique dans l'onglet "Projects" et l'ensemble de ses fichiers dans l'onglet "Files".

  5. Cette dernière étape n'est nécessaire que lorsque NetBeans fonctionne avec le JDK 5. Avec un clic-droit sur le noeud DidacticielProfileur dans la vue "Projects", choisissez "Properties". Dans la partie gauche, sélectionnez "Build", puis "Compiling Sources". Enfin, dans "Additional Compiler Options" saisssez -source 1.4 -target 1.4 et choisissez OK.

Exécution du projet

  1. Il vous faut maintenant noter les identificateurs de tous les processus des machines virtuelles Java en cours d'exécution. Ces identificateurs seront nécessaires pour identifier la JVM utilisée par Tomcat. Sous Unix, la commande suivante permet de récupérer l'identificateur d'un processus: ps -eaf | grep java en ligne de commande:

    $ ps -eaf | grep java
    
    gs 8132 8104 0 12:49:32 pts/4 0:51 /export/home/gs/tools/java/jdk50_01/bin/java -Djdk.home=/export/home/gs/tools/j
    gs 8104 8101 0 12:49:31 pts/4 0:00 /bin/sh ./../platform4/lib/nbexec --jdkhome /export/home/gs/tools/java/jdk50_01
    gs 8215 8093 0 12:50:59 pts/4 0:00 grep java

    Sous Windows, il est possible de récupérer l'identificateur d'un processus à l'aide du Gestionnaire des tâches de Windows tout en ayant pris la peine de faire apparaître les identificateurs. Pour faire apparaître la colonne PID (Identificateur de Processus), choisissez "Affichage", puis "Sélectionner les colonnes..." et enfin cocher la case "PID". L'illustration ci-dessous indique qu'une seule instance de java.exe est en cours d'exécution et que son identificateur est 2848. Gestionnaire des tâches de Windows

    (NdT: avec JDK 5.0, l'exécutable jps (situé dans le répertoire bin du JDK) permet de récupérer simplement les identificateurs de toutes les JVM 5.0 quelque soit le système d'exploitation utilisé.

  2. Dans l'onglet "Projects", cliquez sur le noeud DidacticielProfileur et choisissez "Run Project". Ceci indique à l'EDI d'exécuter le projet en démarrant Tomcat et en affichant la page index.jsp dans une fenêtre de navigateur.

  3. Vérifiez que Tomcat a bien démarré en cliquant sur "Bundled Tomcat" dans l'onglet "Output" qui affiche la sortie standard du serveur. Remontez en haut de cette fenêtre d'information Tomcat pour vérifier que le serveur a bien été démarré avec la JVM du Profileur NetBeans. Pour cela, assurez-vous que la ligne qui débute par Using JAVA_HOME pointe bien vers la JVM du Profileur NetBeans. Dans le cas inverse, ou si Tomcat ne démarre pas, se référer aux instructions de ci-dessus de Configuration de Tomcat. Un exemple de sortie standard sous Windows est présenté ci-dessous . Sortie standard Tomcat

  4. A ce stade, une fenêtre du navigateur doit être ouverte et afficher le contenu de l'adresse http://localhost:8084/DidacticielProfileur/. Pour vérifier le bon fonctionnement de l'application web, tapez NetBeans User dans le champ 'name' et cliquez sur le bouton 'OK'. Le résultat doit ressembler à l'illustration ci-dessous. Fenêtre de navigateur avec l'application Web

Surveillance du comportement de l'application

Se brancher sur une application en cours d'exécution

Maintenant que Tomcat fonctionne correctement avec la JVM fournie par le profileur NetBeans, il est possible de se "brancher" avec les outils de surveillance du comportement de l'application de l'EDI.

  1. Déterminez l'identificateur de processus de la JVM utilisée par Tomcat. A ce stade, il doit y avoir un processus Java (java.exe sous Windows) supplémentaire par rapport à la dernière fois. Le nouveau processus Java doit correspondre à Tomcat (NdT: jps et son compagnon jinfo sont susceptibles de fournir un peu plus d'information sur un processus donné). Notez l'identifiant ainsi récupéré.

  2. Sélectionnez le menu "Profile", puis "Attach Profiler". Pour "Working Directory", saisissez le nom complet du répertoire bin de Tomcat. A titre d'exemple, sous Windows, la valeur doit ressembler à ceci:

    C:\Program Files\netbeans-4.0\nb4.0\jakarta-tomcat-5.0.28\bin

    Et sous Unix :

    /export/home/gs/tools/netbeans/40/nb4.0/jakarta-tomcat-5.0.28/bin
  3. Cliquez maintenant sur "Monitor Application" en vous assurant que l'option "Enable Threads Monitoring" n'est pas sélectionnée.

  4. Cliquez sur le bouton "Attach" et sélectionnez "Manually enter PID of the application". Pour "PID" saisir l'identificateur de processus de la JVM utilisée par Tomcat. Choisissez OK.

Interprétation des graphiques

Le profileur NetBeans présente trois graphiques comme illustré ci-dessous.
Trois Graphiques

Le graphique de gauche est le plus simple à comprendre. La partie rouge indique la quantité de mémoire allouée par la JVM avec une mise à jour toutes les secondes. La partie violette correspond à la quantité de mémoire réellement utilisée. Dans l'exemple ci-dessus, la mémoire allouée est de presque 14Mo dont environ 4Mo sont utilisés par des objets Java.

Le graphique de droite est également simple à comprendre. Il indique simplement le nombre de threads actifs dans la JVM mis à jour toutes les secondes.

Le graphique du ùimmieu est le plus intéressant car il présente deux statistiques importantes.

  • La ligne bleue correspond au temps passé par la JVM à nettoyer sa mémoire avec le ramasse-miettes. Son axe est à droite du graphe. Ce temps nécessaire à la gestion de la mémoire n'est pas disponible pour l'application. Donc, si la ligne bleue indique un pourcentage important de temps passé dans cette gestion de mémoire, il est peut-être nécessaire d'envisager une taille de tas [heap] plus grande (cf. documentation du paramètre -Xmx ) ou bien d'utiliser une autre stratégie de ramasse-miettes.

  • La ligne rouge correspond aux survies de générations. Son axe est sur la gauche du graphe. Cette valeur est l'âge des objets Java contenus dans la mémoire de la JVM. L'âge est ici défini comme le nombre de passage de ramasse-miettes auquel un objet a survécu. Lorsque la valeur des survies est faible, cela indique que la plupart des objets présents en mémoire le sont depuis une durée similaire. Cependant, si cette valeur augmente rapidement dans le temps, cela indique que l'application alloue de nouveaux objets tout en gardant des références vers de nombreux anciens objets. Si ces objets "anciens" ne sont plus réellement nécessaires, alors nous avons à faire à une rétention de mémoire (parfois appelée fuite mémoire).

En appuyant sur le bouton Boutton d'affichage des graphiques de télémétrie l'EDI présente des version plus étendues de ces trois graphiques dans sa page principale.

Détermination de la quantité de temps processeur utilisé par une méthode

Passage au mode d'analyse de performance

A ce stade, le profileur est rattaché à la JVM de Tomcat, mais il ne fournit que des informations macroscopiques. Pour obtenir plus de détails sur la performance d'une méthode donnée (ou de plusieurs méthodes) de votre application, il vous faut modifier les paramètres du profileur.

  1. Appuyez sur le bouton Modification des paramètres du profileur ou choisissez "Profile", puis "Modify Profiling".

  2. Appuyez sur le bouton "Analyze Performance".

  3. Cochez la case "Part of Application" et appuyez sur le bouton "Select" qui se situe à coté.

  4. Appuyez sur le bouton "Add".

  5. Appuyez sur le bouton "Select". Le profileur affiche alors l'arbre de sélection des classes.

  6. Ouvrez le paquetage de sources en cliquant sur son signe plus.

  7. Ouvrez le noeud org.me.hello.

  8. Ouvrez le noeud RequestHandler.java.

  9. Sélectionner le noeud RequestHandler et appuyez sur le bouton OK.

  10. Les méthodes de la classes RequestHandler sont maintenant présentées dans la fenêtre "Select Methods" comme indiqué ci-dessous. Sélectionnez la méthode processRequest et appuyez sur le boutton OK. Fenêtre de sélection des méthodes

  11. Sélectionnez la méthode processRequest en cliquant dessus dans la liste des méthodes racine [root]. Appuyez ensuite sur OK. Vous venez de sélectionner la méthode processRequest comme étant la racine de l'analyse de performance. Ceci signifie que la méthode processRequest et toutes celles qu'elle invoque à son tour (et ainsi de suite) seront surveillées. En partant de processRequest, le profileur procède à la construction du graphe d'appel pour en déduire les méthodes à observer. Le restant de l'application est inchangé et son exécution continuera à se faire sans dégradation de performance.

  12. Appuyez sur le bouton OK de la fenêtre "Modify Profiling".

Exécution de la méthode instrumentée

Maintenant que vous avez positionné processRequest comme méthode racine, il vous faut exercer la partie de l'application qui sollicite cette méthode racine. Dans ce cas précis, c'est assez simple car la méthode processRequest gère toutes les requêtes de la page index.jsp.

  1. Dans votre navigateur, cliquez sur le bouton "Back" pour revenir à la page http://localhost:8084/ProfilingTutorial/.

  2. Cliquez sur le bouton OK pour soumettre un nouveau nom d'utilisateur. La réponse sera un peu plus lente à venir du fait de l'instrumentation de la méthode processRequest.

  3. Une fois que Hello, NetBeans User! s'affiche dans le navigateur, cliquez sur le bouton Bouton de résultats ou choisissez "Profile", puis "Get Current Results".

Interprétation des graphiques de performance

Le profileur affiche les derniers résultats en cours comme illustré ci-dessous.

Résultats de performance.

La fenêtre supérieure présente le graphe d'appel des méthodes en commençant par la méthode racine. Le temps d'exécution de la méthode processRequest est de 168 milli-secondes. Cependant, très peu de temps est réellement passé dans la méthode processRequest elle-même. La sixième ligne dans la fenêtre indique que le "self time" de la méthode processRequest n'est que de 0.421 ms. L'essentiel du temps est passé dans les méthodes appelées par processRequest. Plus précisément, la méthode forward représente 84.7% du temps total d'exécution. Ceci n'est pas surprenant étant donné ce que l'on demande à la méthode forward.

La puissance du profileur NetBeans est de pouvoir vous aider à identifier les goulots d'étranglement dans votre code là où vous ne les attendiez pas et qui risquent de limiter la montée en charge de votre application. On peut également noter que la méthode createUniqueID représente 11.3% du temps total d'exécution. En cliquant sur le signe plus du noeud createUniqueID cela permet d'investiger plus en profondeur pour savoir où est passé ce temps. Si l'on examine le code source de createUniqueID on découvre un algorithme particulièrement inefficace qui doit être revu.

La fenêtre inférieure est une description à plat de la valeur de "self time" de chaque méthode présente dans le graphe.

Surveillance de la création d'objets

Basculement sur le mode d'analyse de la mémoire

Le profileur est capable de faire une analyse détaillée (aussi appelée instrumentation) de l'utilisation des ressources processeur [CPU] ainsi que de l'utilisation de la mémoire, mais il ne peut pas faire les deux au même moment. Pour obtenir les détails sur l'allocation et la destruction par le ramasse-miettes des objets de la JVM, il est nécessaire de modifier les paramètres du profileur.

  1. Cliquez sur le bouton Boutton de modification de propriétés du profileur ou choisissez "Profile", puis "Modify Profiling".

  2. Cliquez sur le bouton "Analyze Memory Usage".

  3. Cochez la case "Record both object creation and garbage collection".

  4. Cliquez sur OK.

Exécution de l'application à analyser

Maintenant que nous avons basculé vers le mode d'analyse de la mémoire, il nous faut réexécuter l'application pour comprendre si sa consommation mémoire est normale et efficace.

  1. Dans une fenêtre de navigateur, revenez en arrière sur la page http://localhost:8084/ProfilingTutorial/.

  2. Appuyez sur le bouton "OK" pour soumettre de nouveau le nom d'utilisateur "NetBeans User".

  3. Une fois Hello, NetBeans User! affiché dans le navigateur, appuyez de nouveau sur le bouton de retour en arrière pour revenir sur cette page.

  4. Répétez ces étapes 2 et 3 neuf fois. Il est important pour les besoins de la démonstration que le même nom d'utilisateur soit à chaque fois utilisé.

  5. Cliquez sur le bouton Bouton des résultats ou choisissez "Profile", puis "Get Current Results".

  6. Le profileur affiche les derniers résultats de mémoire dans un tableau. Cliquez sur la colonne "Class Name" pour trier les lignes selon le nom des classes.

  7. Naviguez vers le bas jusqu'à la ligne de la classe org.me.hello.NameHandler.

Interprétation des graphiques de la mémoire

L'illustration ci-dessous présente les statistiques de la classe org.me.hello.NameHandler. Résultats mémoire.

Les colonnes fournissent des informations sur l'allocation de l'objet et son occupation mémoire.

  • "Total alloc. obj." (à droite) est simple à comprendre. Il s'agit du nombre total d'objets instanciés pour la classe en question. Pour la classe NameHandler, cette valeur est de 10, ce qui correspond au nombre de fois ou vous avez appuyé sur OK dans l'application.

  • "Allocated Objects" correspond au nombre d'objets que le profileur surveille. Dans cet exemple, sur les 1 instances de NameHandler créées, le profileur n'en surveille qu'une seule. Par défaut, ce nombre est environ 10% du nombre total d'objets alloués (les valeurs que vous observez seront donc peut-être légèrement différentes). En ne surveillant qu'un sous-ensemble des objets créés, le profileur est capable de réduire de manière très sensible la dégradation de performance de la JVM liée à toute technique d'instrumentation. L'application s'exécute quasiment à vitesse réelle.

  • "Live Objects" représente le nombre d'objets alloués dans le tas [heap] de la JVM et qui par conséquent occupent de la mémoire.

  • Les deux colonnes "Live Bytes" présentent la quantité de mémoire utilisées par les objets décrits ci-dessus. La première colonne propose un graphe, la seconde une version texte.

  • La valeur "Age Average" est calculée en utilisant "Live Objects". L'âge de chaque objet correspond au nombre de passage du ramasse-miettes auxquels il a survécu. La somme de tous les âges divisé par le nombre d'objets correspond la moyenne d'âge [Age Average].

  • La valeur "Generations" est calculée à partir des "Live Objects". La définition de la moyenne d'âge reste la même et "Generations" est le nombre d'âges différents répertoriés pour tous les objets.

Cette information doit vous permettre de remonter à la source d'une rétention de mémoire. La valeur "Generations" est la même que "Surviving Generations" décrite dans le paragraphe Interprétation des graphiques, mais ramenée à une seule classe. Cette meilleure granularité doit vous aider à identifier les objets qui occupent inutilement de la mémoire.

Dans notre exemple, il n'est pas nécessaire d'observer "Generations" pour identifier le problème dans la classe NameHandler. La valeur "alloc. obj." est 10. Le principe du code de la servlet était de réutiliser des instances d'objets NameHandler pour y stocker le nom saisi par l'utilisateur. Il est clair ici que cela ne fonctionne pas. Le bug provient de la méthode processRequest : la clé utilisée pour vérifier l'existence d'un élément dans la Map n'est pas la même que celle utilisée pour stocker l'élément. Le résultat est une rétention de mémoire relativement classique en Java : un élément placé dans un objet Map est aussitôt oublié.

Le profileur fournit également une autre fonctionnalité qui permet d'identifier l'endroit dans votre code où un objet donné est alloué. Cela peut permettre de comprendre pourquoi un objet est toujours référencé en mémoire. Pour cela, le profileur propose des graphes d'appel de méthodes pour toute classe. Il suffit de sélectionner "Show Allocations Stack Traces" dans le menu contextuel (clic-droit) pour n'importe quelle classe du tableau.

Pour mettre fin à la session, choisissez "Profile", puis "Detach".

Pour aller plus loin

L'aide en ligne du Profileur vous propose de l'aide supplémentaire sur ces différents sujets:

  • Il est possible de filtrer les données affichées pendant une analyse de l'usage du temps machine afin d'éviter d'analyser les méthodes qui n'appartiennent pas à votre application (par exemple toutes les méthodes java.*). Ceci permet de garder des performances proches de celles de l'application non-instrumentée.

  • La fonctionnalité d'analyse de l'usage de la mémoire peut se restreindre au phénomène d'allocation de nouveaux objets. Là aussi, l'impact en performance du profileur sera minimisé.

  • Si, en effectuant une analyse de l'usage de la mémoire, vous constatez que seules quelques classes sont susceptibles d'en être la cause, vous pouvez supprimer la surveillance de toutes les autres classes.

  • Il est également possible de mesurer le temps de démarrage d'une application ou bien seulement un fragment de code de votre application.

  • Des informations dédiées à chaque thread sont également disponibles.



Companion
Projects:
MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Virtual Box - full virtualizer  Open ESB - The Open Enterprise Service Bus Powered by