web-dev-qa-db-fra.com

Comment analyser un dump de threads Java?

J'essaie de mieux comprendre Java, en particulier la gestion de la mémoire et les threads. C'est pour cette raison que j'ai récemment trouvé un intérêt pour la recherche de décharges de threads.

Voici quelques lignes tirées d'une application Web utilisant VisualVM, un outil intégré à Java:

"Finalizer" daemon prio=8 tid=0x02b3d000 nid=0x898 in Object.wait() [0x02d0f000]
   Java.lang.Thread.State: WAITING (on object monitor)
    at Java.lang.Object.wait(Native Method)
    - waiting on <0x27ef0288> (a Java.lang.ref.ReferenceQueue$Lock)
    at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:118)
    - locked <0x27ef0288> (a Java.lang.ref.ReferenceQueue$Lock)
    at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:134)
    at Java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.Java:159)

   Locked ownable synchronizers:
    - None

"Reference Handler" daemon prio=10 tid=0x02b3b800 nid=0x494 in Object.wait() [0x02cbf000]
   Java.lang.Thread.State: WAITING (on object monitor)
    at Java.lang.Object.wait(Native Method)
    - waiting on <0x27ef0310> (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 <0x27ef0310> (a Java.lang.ref.Reference$Lock)

J'ai d'abord des questions sur certains noms de variables:

  • que signifient tid et nid?
  • Quelle est la figure entre parenthèses carrées après Object.wait?

Ensuite, pour la trace elle-même:

  • qu'est-ce que cela signifie attendre <.....> (un fichier Java.lang ....) et quel est le nombre en <..>
  • qu'est-ce que cela signifie verrouillé <.....> (un Java.lang ....) même question, qu'est-ce qu'il y a <..>

Je pensais que la Parole verrouillée était liée d'une manière ou d'une autre à une condition d'attente. Cependant, j'avais tort. En fait, je me demande pourquoi lock est répété trois fois, mais le thread est dans un état exécutable, comme indiqué dans le même dump:

"Thread-0" prio=6 tid=0x02ee3800 nid=0xc1c runnable [0x03eaf000]
   Java.lang.Thread.State: RUNNABLE
    at Java.io.FileInputStream.readBytes(Native Method)
    at Java.io.FileInputStream.read(FileInputStream.Java:199)
    at Java.io.BufferedInputStream.read1(BufferedInputStream.Java:256)
    at Java.io.BufferedInputStream.read(BufferedInputStream.Java:317)
    - locked <0x23963378> (a Java.io.BufferedInputStream)
    at Sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.Java:264)
    at Sun.nio.cs.StreamDecoder.implRead(StreamDecoder.Java:306)
    at Sun.nio.cs.StreamDecoder.read(StreamDecoder.Java:158)
    - locked <0x23968450> (a Java.io.InputStreamReader)
    at Java.io.InputStreamReader.read(InputStreamReader.Java:167)
    at Java.io.BufferedReader.fill(BufferedReader.Java:136)
    at Java.io.BufferedReader.readLine(BufferedReader.Java:299)
    - locked <0x23968450> (a Java.io.InputStreamReader)
    at Java.io.BufferedReader.readLine(BufferedReader.Java:362)
    at org.codehaus.plexus.util.cli.StreamPumper.run(StreamPumper.Java:145)

Enfin, c'était le pire d'entre eux:

"CompilerThread0" daemon prio=10 tid=0x02b81000 nid=0x698 waiting on condition [0x00000000]
   Java.lang.Thread.State: RUNNABLE

Ce fil est en état d'exécution, mais il est en attente. Quelle condition et quelle est 0x00000?

Pourquoi la trace de la pile est-elle si courte sans aucune preuve de la classe de thread?

Si vous pouviez répondre à toutes mes questions, je vous en serais très reconnaissant.

Merci

93
Leonardo

Le TID est l'identifiant et le NID est: ID de thread natif. Cet identifiant dépend fortement de la plateforme. C'est le NID dans les vidages de threads jstack. Sous Windows, il s'agit simplement de l'ID de thread au niveau du système d'exploitation au sein d'un processus. Sous Linux et Solaris, il s’agit du PID du thread (qui est un processus léger). Sur Mac OS X, il s’agit de la valeur native pthread_t.

Cliquez sur le lien suivant: ID de thread au niveau Java : pour obtenir une définition et une explication plus détaillée de ces deux termes.

Sur le site d'IBM, j'ai trouvé ce lien: Comment interpréter un vidage de thread . cela couvre cela plus en détail:

Il explique ce que signifie attendre: Un verrou empêche plusieurs entités d'accéder à une ressource partagée. Chaque objet en Java ™ est associé à un verrou (obtenu en utilisant un bloc ou une méthode synchronisé). Dans le cas de la machine virtuelle Java, les threads se font concurrence pour diverses ressources de la machine virtuelle et se verrouillent sur des objets Java.

Ensuite, il décrit le moniteur comme un type spécial de mécanisme de verrouillage utilisé dans la machine virtuelle Java pour permettre une synchronisation flexible entre les threads. Pour les besoins de cette section, lisez les termes surveiller et verrouiller de manière interchangeable.

Puis ça va plus loin:

Pour éviter d'avoir un moniteur sur chaque objet, la JVM utilise généralement un indicateur dans un bloc de classe ou de méthode pour indiquer que l'élément est verrouillé. La plupart du temps, un morceau de code transitera une section verrouillée sans contention. Par conséquent, le drapeau de gardien suffit à protéger ce code. Ceci s'appelle un moniteur plat. Cependant, si un autre thread souhaite accéder à du code verrouillé, un conflit réel s'est produit. La machine virtuelle Java doit maintenant créer (ou gonfler) l'objet de contrôle pour contenir le deuxième thread et organiser un mécanisme de signalisation pour coordonner l'accès à la section de code. Ce moniteur est maintenant appelé moniteur gonflé.

Voici une explication plus détaillée de ce que vous voyez sur les lignes du vidage de thread. Un thread Java est implémenté par un thread natif du système d'exploitation. Chaque thread est représenté par une ligne en gras telle que:

"Thread-1" (TID: 0x9017A0, sys_thread_t: 0x23EAC8, état: R, ID natif: 0x6E4) prio = 5

* Les 6 éléments suivants expliquent cela comme je les ai trouvés dans l'exemple, les valeurs entre crochets []:

  1. name [ Thread-1 ],
  2. identifiant [ 0x9017A0 ],
  3. Adresse de structure de données JVM [ 0x23EAC8 ],
  4. état actuel [ [~ # ~] r [~ # ~] ],
  5. identifiant de thread natif [ 0x6E4 ],
  6. et priorité [ 5 ].

"Wait on" semble être un thread démon associé au jvm lui-même et non au thread d'application. Lorsque vous obtenez un "dans Object.wait ()", cela signifie que le thread démon, "finaliseur" ici, attend une notification concernant le verrouillage d'un objet. Dans ce cas, il vous indique quelle notification il attend: "- en attente de <0x27ef0288> (un verrou Java.lang.ref.ReferenceQueue $) "

La définition de la référenceQueue est: Les files d'attente de référence, auxquelles les objets de référence enregistrés sont ajoutés par le ramasse-miettes après la détection des modifications d'accessibilité appropriées.

Le processus de finalisation s'exécute de manière à ce que la collecte de place fonctionne pour nettoyer les ressources associées à un objet. Si je le vois correctement, le finaliseur ne peut pas verrouiller cet objet: Java.lang.ref.ReferenceQueue.remove (ReferenceQueue.Java:118) car l'objet Java est en exécutant une méthode, le thread de finalisation est verrouillé jusqu'à ce que l'objet soit terminé avec la tâche en cours.

En outre, le finaliseur ne cherche pas uniquement à récupérer de la mémoire, il est plus impliqué que cela pour le nettoyage des ressources. Je dois faire plus d’études là-dessus, mais si vous avez des fichiers ouverts, des sockets, etc. liés à une méthode d’objet, le finaliseur travaillera à libérer ces éléments.

Quelle est la figure entre parenthèses au carré après Object.wait dans le vidage de thread?

C'est un pointeur en mémoire sur le fil. Voici une description plus détaillée:

C.4.1 Informations sur le fil

La première partie de la section de thread montre le thread qui a provoqué l'erreur fatale, comme suit:

Current thread (0x0805ac88):  JavaThread "main" [_thread_in_native, id=21139]
                    |             |         |            |          +-- ID
                    |             |         |            +------------- state
                    |             |         +-------------------------- name
                    |             +------------------------------------ type
                    +-------------------------------------------------- pointer

Le pointeur de thread est le pointeur vers la structure de thread interne Java VM. Elle n'a généralement aucun intérêt si vous ne déboguez pas un live Java VM ou fichier principal.

Cette dernière description provient de: Guide de dépannage pour Java SE 6 avec HotSpot VM

Voici quelques liens supplémentaires sur les vidages de threads:

108
James Drinkard

Suite à l'excellente réponse de @James Drinkard:

Notez que, selon l'implémentation sous-jacente, le Java.lang.Thread.State d'un thread bloqué dans une méthode native peut être signalé sous la forme RUNNABLE, où A thread in the runnable state is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.

Il s'avère que cette description comprend également le fait d'être bloqué dans un appel de système d'exploitation, tel qu'une opération d'interrogation ou de lecture, probablement parce qu'il n'y a aucune garantie que la machine virtuelle Java puisse savoir quand un appel de méthode natif a été bloqué au niveau du système d'exploitation.

De nombreuses discussions sur les vidages de threads JVM que j'ai observées ignorent complètement cette possibilité ou la parcourent allègrement sans en prendre en compte les implications. Notamment, les outils de surveillance peuvent signaler de manière confuse que plusieurs de ces threads sont en cours d'exécution. ils fonctionnent tous à 100%.

10
Jeremy