IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Obtenir un fichier heapdump pendant l'exécution d'une appli Java


Sujet :

Java

  1. #1
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Par défaut Obtenir un fichier heapdump pendant l'exécution d'une appli Java
    Bonjour,

    j'ai codé un petit serveur de jeu en Java. Tout fonctionne à merveille mais si je laisse tourner le serveur plus d'une semaine, je finis par me retrouver avec une OutOfMemory Exception. Je dois donc avoir une jolie petite fuite mémoire quelque part et une revue de mon code ne m'a pas permis d'en trouver l'origine.

    Du coup, je me suis penché sur le moyen de générer des heapDumps de mon appli java en cours d'exécution (en prod). J'aimerais en effet pouvoir récupérer des infos concernant le nombre d'instances de chaque classe présentes dans la JVM à différents moments (et donc voir celles qui s'accumulent sans être libérées).

    Après quelques recherches, je suis tombé sur l'article "How to obtain an IBM JVM heapdump" sur lequel je me suis basé pour me faire un petit script pour lancer mon serveur sous linux:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    clear
    export IBM_HEAP_DUMP=true
    export IBM_HEAPDUMP=true
    export IBM_HEAPDUMPDIR=/home/mylogin/myServer/
    export JAVA_DUMP_OPTS=ONANYSIGNAL\(ALL\)
     
    java -cp myServer.jar myPackage.MyServerClass
    Je récupère ensuite le PID de ma JVM avec un
    et j'envoie le signal QUIT à la JVM pour lui demander de générer ledit heapDump:
    Sur la sortie standard de mon serveur, j'obtiens alors une sorte de résumé qui me renseigne surtout sur les threads en cours d'exécution (cf ci-dessous).


    Mais:

    1/ Rien en ce qui concerne l'utilisation détaillée de la mémoire (quelles types d'objets, nombre d'instances) hormis un chiffre global

    2/ Aucun fichier n'est généré à l'issue de cet appel alors que l'article en lien dit qu'on est censé générer un fichier de type PHD qu'on peut ensuite exploiter avec un programme tiers.

    Si vous avez une idée, je suis preneur...
    Merci d'avance


    Full thread dump Java HotSpot(TM) Client VM (11.2-b01 mixed mode, sharing):

    "DestroyJavaVM" prio=10 tid=0xb4f3fc00 nid=0x66a2 waiting on condition [0x00000000..0xb76bc070]
    java.lang.Thread.State: RUNNABLE

    "Thread-3" prio=10 tid=0xb4f27000 nid=0x66bb waiting on condition [0xb4c42000..0xb4c42eb0]
    java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at gameserver.db.MysqlConnexion.run(MysqlConnexion.java:117)
    at java.lang.Thread.run(Thread.java:619)

    "Thread-2" prio=10 tid=0xb4f27c00 nid=0x66b7 runnable [0xb4c93000..0xb4c93e30]
    java.lang.Thread.State: RUNNABLE
    at java.net.PlainSocketImpl.socketAccept(Native Method)
    at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:384)
    - locked <0x8ba40078> (a java.net.SocksSocketImpl)
    at java.net.ServerSocket.implAccept(ServerSocket.java:453)
    at java.net.ServerSocket.accept(ServerSocket.java:421)
    at gameserver.webconnector.WebServerModule.run(WebServerModule.java:86)
    at java.lang.Thread.run(Thread.java:619)

    "Thread-0" prio=10 tid=0xb4f20c00 nid=0x66b6 runnable [0xb4ce4000..0xb4ce4db0]
    java.lang.Thread.State: RUNNABLE
    at java.net.PlainSocketImpl.socketAccept(Native Method)
    at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:384)
    - locked <0x8ba40240> (a java.net.SocksSocketImpl)
    at java.net.ServerSocket.implAccept(ServerSocket.java:453)
    at java.net.ServerSocket.accept(ServerSocket.java:421)
    at network.MsgServerSocket.run(MsgServerSocket.java:73)

    "TimerQueue" daemon prio=10 tid=0xb4f34000 nid=0x66b5 in Object.wait() [0xb4d35000..0xb4d36130]
    java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x8ba403c8> (a javax.swing.TimerQueue)
    at javax.swing.TimerQueue.run(TimerQueue.java:236)
    - locked <0x8ba403c8> (a javax.swing.TimerQueue)
    at java.lang.Thread.run(Thread.java:619)

    "Low Memory Detector" daemon prio=10 tid=0x0808e400 nid=0x66a8 runnable [0x00000000..0x00000000]
    java.lang.Thread.State: RUNNABLE

    "CompilerThread0" daemon prio=10 tid=0x0808c400 nid=0x66a7 waiting on condition [0x00000000..0xb519ca48]
    java.lang.Thread.State: RUNNABLE

    "Signal Dispatcher" daemon prio=10 tid=0x0808ac00 nid=0x66a6 waiting on condition [0x00000000..0x00000000]
    java.lang.Thread.State: RUNNABLE

    "Finalizer" daemon prio=10 tid=0x08082000 nid=0x66a5 in Object.wait() [0xb53ef000..0xb53efeb0]
    java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x8be522e8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116)
    - locked <0x8be522e8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)

    "Reference Handler" daemon prio=10 tid=0x08080c00 nid=0x66a4 in Object.wait() [0xb5440000..0xb5440e30]
    java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x8be52370> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:485)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
    - locked <0x8be52370> (a java.lang.ref.Reference$Lock)

    "VM Thread" prio=10 tid=0x0807f000 nid=0x66a3 runnable

    "VM Periodic Task Thread" prio=10 tid=0x08091400 nid=0x66a9 waiting on condition

    JNI global references: 1035

    Heap
    def new generation total 960K, used 567K [0x8b960000, 0x8ba60000, 0x8be40000)
    eden space 896K, 56% used [0x8b960000, 0x8b9ddca8, 0x8ba40000)
    from space 64K, 100% used [0x8ba40000, 0x8ba50000, 0x8ba50000)
    to space 64K, 0% used [0x8ba50000, 0x8ba50000, 0x8ba60000)
    tenured generation total 4096K, used 638K [0x8be40000, 0x8c240000, 0x8f960000)
    the space 4096K, 15% used [0x8be40000, 0x8bedfb38, 0x8bedfc00, 0x8c240000)
    compacting perm gen total 12288K, used 2158K [0x8f960000, 0x90560000, 0x93960000)
    the space 12288K, 17% used [0x8f960000, 0x8fb7ba68, 0x8fb7bc00, 0x90560000)
    ro space 8192K, 74% used [0x93960000, 0x93f580d8, 0x93f58200, 0x94160000)
    rw space 12288K, 58% used [0x94160000, 0x94873618, 0x94873800, 0x94d60000)

  2. #2
    Rédacteur
    Avatar de CyberChouan
    Homme Profil pro
    Directeur technique
    Inscrit en
    Janvier 2007
    Messages
    2 752
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Janvier 2007
    Messages : 2 752
    Par défaut
    Tu peux faire ça en utilisant des outils de type "Profiler" (VisualVM, etc.).

    A l'époque où j'avais rencontré une fuite mémoire en production, j'en avais découvert l'origine grâce à l'excellent JProbe (mais le grand groupe pour lequel je travaillais en avais des licences qui ne sont pas données ).
    Avant de poster, pensez à regarder la FAQ, les tutoriaux, la Javadoc (de la JRE que vous utilisez) et à faire une recherche
    Je ne réponds pas aux questions techniques par MP: les forums sont faits pour ça
    Mes articles et tutoriaux & Mon blog informatique

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 28
    Par défaut
    Il y a aussi l'utilitaire jmap livré dans le répertoire bin du JDK.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    jmap -dump:format=b,file=monfichier.dump,live <pid>
    Ensuite, tu utilises jhat (toujours dans le bin du JDK), lequel démarre un petit serveur http :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    jhat monfichier.dump
    Tu devrais voir un truc du genre "Server is running on port 7000" un fois que c'est prêt et tu n'as plus qu'à explorer la mémoire avec un navigateur en allant sur http://localhost:7000

    Ce profiler de mémoire est très rudimentaire mais très simple d'utilisation. Un seul piège toutefois : dans les différentes pages web, cliquer sur le nom d'une classe va te montrer l'instance de l'objet Class<?> correspondant et non l'objet instance de cette classe.

  4. #4
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par CyberChouan Voir le message
    Tu peux faire ça en utilisant des outils de type "Profiler" (VisualVM, etc.).

    A l'époque où j'avais rencontré une fuite mémoire en production, j'en avais découvert l'origine grâce à l'excellent JProbe (mais le grand groupe pour lequel je travaillais en avais des licences qui ne sont pas données ).
    +1 il est très bon pour aider à trouver les fuites de mémoire. La licence n'est pas excessive pour un poste. Et en cas d'équipe de petite taille on peux faire "circuler" la licence, puisqu'il s'agit d'un outil à usage occasionnel. Il faut juste le désinstaller d'un poste avant de l'installer sur un autre

    Il y a aussi l'option démo 100% fonctionnelle pour 30 jours si ma mémoire est bonne.

  5. #5
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Par défaut
    Merci pour toutes vos propositions

    Je vais jeter un oeil aux différentes solutions et je vous donnerai un feedback.

  6. #6
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Par défaut
    re,

    pour le feedback: j'ai finalement opté pour les outils inclus de base dans Java, à savoir jmap pour générer le fichier dump sur mon serveur de prod et jhat pour naviguer dedans une fois le dump rapatrié dans mon environnement de dév.

    Cela m'a déjà permis:

    - de fixer un memory leak dans mon code (une hashmap dans laquelle j'insérais des éléments sans jamais les retirer une fois qu'ils n'étaient plus utilisés)

    - et je suis en train de tester un deuxième fix : des createStatement() pour une connexion JDBC qui n'avaient pas de stmt.close() en face.


    PS: le code Java en question concerne le serveur de mon petit projet en signature

  7. #7
    Rédacteur
    Avatar de CyberChouan
    Homme Profil pro
    Directeur technique
    Inscrit en
    Janvier 2007
    Messages
    2 752
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Janvier 2007
    Messages : 2 752
    Par défaut
    Je rebondis sur ton feedback : pour les connexions ouvertes sans être fermées, il existe un excellent outil d'analyse OpenSource (mais qui n'a plus rien à voir avec le profiling).

    Il s'agit de FindBugs. Il analyse le bytecode compilé de tes classes à la recherches de bugs "classiques", tels que les connexions ouvertes mais non fermées, etc.

    Note : encore au dessus, il y a Sonar qui intègre les résultats de Findbugs (et CheckStyle, PMD, etc.), mais ça devient peut-être "usine à gaz" par rapport à ton besoin.
    Avant de poster, pensez à regarder la FAQ, les tutoriaux, la Javadoc (de la JRE que vous utilisez) et à faire une recherche
    Je ne réponds pas aux questions techniques par MP: les forums sont faits pour ça
    Mes articles et tutoriaux & Mon blog informatique

  8. #8
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Par défaut
    Citation Envoyé par CyberChouan Voir le message
    Il s'agit de FindBugs. Il analyse le bytecode compilé de tes classes à la recherches de bugs "classiques", tels que les connexions ouvertes mais non fermées, etc.
    Je ne connaissais pas, merci beaucoup, j'y jetterai un coup d'oeil dès que j'aurai un peu plus de temps à moi.

    Au passage, pour jHat, il me restait un mystère que je viens de lever: certaines classes dans les rapports avaient des noms bizarre, genre [B, [C, etc...
    J'ai trouvé la réponse: il s'agit en fait de tableaux de primitives.
    Those are arrays of primitives ([B == byte[], [C == char, [I == int). [Lx; is an array of type x.
    Enfin, pour la petite histoire, mes memory leaks restants étaient bien en relation avec ma connexion JDBC vers ma base MySQL. Depuis que je fais bien un resultSet.close() et un Statement.close() pour chaque requête, le serveur tourne comme une horloge.

Discussions similaires

  1. [XL-2003] Masquer l'ouverture de fichiers pendant l'exécution d'une macro
    Par thibane dans le forum Macros et VBA Excel
    Réponses: 5
    Dernier message: 30/04/2009, 08h10
  2. [Oracle] Obtenir un fichier .xls à partir du résultat d'une requête
    Par alyon dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 11/07/2008, 08h39
  3. Intercepter un clic dans un userform pendant l'exécution d'une macro
    Par ouskel'n'or dans le forum Macros et VBA Excel
    Réponses: 21
    Dernier message: 05/04/2008, 14h40
  4. Réponses: 0
    Dernier message: 26/03/2008, 11h20
  5. Afficher un waitbar pendant l'exécution d'une boucle while
    Par LMU2S dans le forum Interfaces Graphiques
    Réponses: 1
    Dernier message: 18/03/2008, 19h22

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo