Comment analyser un dump de thread java?

100

J'essaie de mieux comprendre java, en particulier sur la gestion de la mémoire et les threads. Pour cette raison, je me suis récemment intéressé aux décharges de threads.

Voici quelques lignes tirées d'une application Web utilisant VisualVM, un outil intégré pour 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 signifie tid et nid?
  • Quelle est la figure entre parenthèses carrées après Object.wait?

Ensuite, pour la trace de pile elle-même:

  • qu'est-ce que cela signifie attendre <.....> (un java.lang ....) et quel est le nombre dans <..>
  • qu'est-ce que cela signifie verrouillé <.....> (un java.lang ....) même question, que contient <..>

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

"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 thread est à l'état exécutable, mais il attend sous condition. Quelle condition et qu'est-ce que 0x00000?

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

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

Merci

Léonard
la source

Réponses:

113

Le TID est l'ID de tête et le NID est: l'ID de thread natif. Cet ID dépend fortement de la plate-forme. C'est le NID dans les dumps de thread jstack. Sous Windows, il s'agit simplement de l'ID de thread au niveau du système d'exploitation dans un processus. Sur Linux et Solaris, c'est le PID du thread (qui à son tour est un processus léger). Sur Mac OS X, on dit qu'il s'agit de la valeur native pthread_t.

Allez sur ce lien: ID de thread au niveau Java : pour 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 thread dump . qui couvre cela plus en détail:

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

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

Ensuite, cela 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 par une section verrouillée sans conflit. Par conséquent, le drapeau de gardien suffit à protéger ce morceau de code. C'est ce qu'on appelle un moniteur plat. Toutefois, si un autre thread souhaite accéder à un code verrouillé, une véritable contention s'est produite. La JVM doit maintenant créer (ou gonfler) l'objet moniteur 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é un moniteur gonflé.

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

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

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

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

Le "wait on" semble être un thread démon associé au jvm lui-même et non au thread d'application perse. Lorsque vous obtenez un "in Object.wait ()", cela signifie que le thread démon, "finalizer" ici, attend une notification concernant un verrou sur un objet, dans ce cas, il vous montre quelle notification il attend: "- en attente sur <0x27ef0288> (un java.lang.ref.ReferenceQueue $ Lock) "

La définition de ReferenceQueue est la suivante: Files d'attente de référence, auxquelles les objets de référence enregistrés sont ajoutés par le garbage collector après la détection des modifications d'accessibilité appropriées.

Le thread du finaliseur s'exécute afin que le garbage collection opère pour nettoyer les ressources associées à un objet. Si je le vois correctement, le finaliseur ne peut pas obtenir le verrou de cet objet: java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:118) car l'objet java exécute une méthode, donc le thread du finaliseur est verrouillé jusqu'à ce que cet objet ait terminé sa tâche en cours.

De plus, le finaliseur ne cherche pas seulement à récupérer de la mémoire, il est plus impliqué que celui de nettoyer les ressources. J'ai besoin de faire plus d'études à ce sujet, mais si vous avez des fichiers ouverts, des sockets, etc. liés à des méthodes d'objets, le finaliseur travaillera également à libérer ces éléments.

Quelle est la figure entre parenthèses carrées après Object.wait dans le thread dump?

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

C.4.1 Informations sur les fils

La première partie de la section 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 de Java VM. Cela ne présente généralement aucun intérêt, sauf si vous déboguez une VM Java ou un fichier core en direct.

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

Voici quelques autres liens sur les vidages de threads:

James Drinkard
la source
11

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

Notez que, en fonction de l'implémentation sous-jacente, le java.lang.Thread.State d'un thread bloqué dans une méthode native peut être signalé comme 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 englobe également le blocage 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 JVM puisse savoir quand un appel de méthode native s'est bloqué au niveau du système d'exploitation.

De nombreuses discussions sur les décharges de threads JVM que j'ai vues ignorent complètement cette possibilité ou la survolent allègrement sans en considérer les implications - la moindre étant que les outils de surveillance peuvent indiquer de manière confuse que plusieurs de ces threads sont `` en cours d'exécution '', et de plus que ils fonctionnent tous à 100%.

Jérémie
la source