Wie analysiere ich einen Java-Thread-Dump?

100

Ich versuche mehr über Java zu verstehen, insbesondere über Speicherverwaltung und Threads. Aus diesem Grund habe ich kürzlich Interesse an Thread-Dumps gefunden.

Hier sind einige Zeilen aus einer Web-App mit VisualVM, einem integrierten Tool für 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)

Zuerst habe ich Fragen zu einigen Variablennamen:

  • was bedeutet tid und nid?
  • Was ist die Zahl in eckigen Klammern nach Object.wait?

Dann für den Stack-Trace selbst:

  • Was bedeutet es, auf <.....> (a java.lang ....) zu warten und wie lautet die Nummer in <..>
  • Was bedeutet es gesperrt <.....> (eine java.lang ....) gleiche Frage, was ist in <..>

Ich dachte, dass das gesperrte Wort in irgendeiner Weise mit einer Wartebedingung zusammenhängt, aber ich habe mich geirrt. Tatsächlich frage ich mich, warum das Sperren dreimal wiederholt wird, aber der Thread befindet sich im ausführbaren Zustand, wie im selben Speicherauszug gezeigt:

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

Als letztes war dies das Schlimmste von ihnen:

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

Dieser Thread befindet sich im ausführbaren Zustand, wartet jedoch unter bestimmten Bedingungen. Welcher Zustand und was ist 0x00000?

Warum ist der Stack-Trace so kurz ohne Hinweise auf die Thread-Klasse?

Wenn Sie alle meine Fragen beantworten könnten, wäre ich Ihnen sehr dankbar.

Vielen Dank

Leonardo
quelle

Antworten:

113

Die TID ist die ID des Kopfes und die NID lautet: Native Thread-ID. Diese ID ist stark plattformabhängig. Es ist die NID in Jstack-Thread-Dumps. Unter Windows ist es einfach die Thread-ID auf Betriebssystemebene innerhalb eines Prozesses. Unter Linux und Solaris ist dies die PID des Threads (was wiederum ein leichter Prozess ist). Unter Mac OS X wird dies als nativer pthread_t-Wert bezeichnet.

Gehen Sie zu diesem Link: Thread-ID auf Java-Ebene : Für eine Definition und eine weitere Erläuterung dieser beiden Begriffe.

Auf der IBM Website habe ich diesen Link gefunden: So interpretieren Sie einen Thread-Dump . das deckt dies genauer ab:

Es wird erklärt, was dieses Warten bedeutet: Eine Sperre verhindert, dass mehr als eine Entität auf eine gemeinsam genutzte Ressource zugreift. Jedem Objekt in Java ™ ist eine Sperre zugeordnet (mithilfe eines synchronisierten Blocks oder einer synchronisierten Methode). Bei der JVM konkurrieren Threads um verschiedene Ressourcen in der JVM und sperren Java-Objekte.

Anschließend wird der Monitor als eine spezielle Art von Sperrmechanismus beschrieben, der in der JVM verwendet wird, um eine flexible Synchronisation zwischen Threads zu ermöglichen. Lesen Sie für den Zweck dieses Abschnitts die Begriffe Monitor und Sperre austauschbar.

Dann geht es weiter:

Um zu vermeiden, dass für jedes Objekt ein Monitor vorhanden ist, verwendet die JVM normalerweise ein Flag in einem Klassen- oder Methodenblock, um anzuzeigen, dass das Element gesperrt ist. Meistens durchläuft ein Code einen gesperrten Abschnitt ohne Konkurrenz. Daher reicht die Wächterflagge aus, um diesen Code zu schützen. Dies wird als Flachbildschirm bezeichnet. Wenn jedoch ein anderer Thread auf einen gesperrten Code zugreifen möchte, ist ein echter Konflikt aufgetreten. Die JVM muss nun das Monitorobjekt erstellen (oder aufblasen), um den zweiten Thread zu halten, und einen Signalisierungsmechanismus einrichten, um den Zugriff auf den Codeabschnitt zu koordinieren. Dieser Monitor wird jetzt als aufgeblasener Monitor bezeichnet.

Hier finden Sie eine ausführlichere Erklärung dessen, was Sie in den Zeilen des Thread-Dumps sehen. Ein Java-Thread wird von einem nativen Thread des Betriebssystems implementiert. Jeder Thread wird durch eine fett gedruckte Linie dargestellt, z.

"Thread-1" (TID: 0x9017A0, sys_thread_t: 0x23EAC8, Status: R, native ID: 0x6E4) prio = 5

* Die folgenden 6 Punkte erklären dies, da ich sie aus dem Beispiel mit den Werten in Klammern [] abgeglichen habe:

  1. Name [ Thread-1 ],
  2. Kennung [ 0x9017A0 ],
  3. JVM-Datenstrukturadresse [ 0x23EAC8 ],
  4. aktueller Zustand [ R ],
  5. native Thread- ID [ 0x6E4 ],
  6. und Priorität [ 5 ].

Das "Warten auf" scheint ein Daemon-Thread zu sein, der dem JVM selbst zugeordnet ist, und nicht der Anwendungs-Thread Perse. Wenn Sie ein "in Object.wait ()" erhalten, bedeutet dies, dass der Daemon-Thread "finalizer" hier auf eine Benachrichtigung über eine Sperre für ein Objekt wartet. In diesem Fall wird angezeigt, auf welche Benachrichtigung es wartet: "- Warten auf <0x27ef0288> (eine java.lang.ref.ReferenceQueue $ Lock) "

Die Definition der ReferenceQueue lautet: Referenzwarteschlangen, an die registrierte Referenzobjekte vom Garbage Collector angehängt werden, nachdem die entsprechenden Änderungen der Erreichbarkeit festgestellt wurden.

Der Finalizer-Thread wird ausgeführt, sodass die Garbage Collection die mit einem Objekt verknüpften Ressourcen bereinigt. Wenn ich es richtig sehe, kann der Finalizer die Sperre für dieses Objekt nicht erhalten: java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:118), da das Java-Objekt eine Methode ausführt, also der Finalizer-Thread gesperrt, bis das Objekt mit seiner aktuellen Aufgabe fertig ist.

Außerdem möchte der Finalizer nicht nur Speicher zurückgewinnen, sondern auch mehr Ressourcen bereinigen. Ich muss mehr darüber lernen, aber wenn Sie Dateien geöffnet haben, Sockets usw., die sich auf Objektmethoden beziehen, wird der Finalizer auch daran arbeiten, diese Elemente freizugeben.

Was ist die Zahl in eckigen Klammern nach Object.wait im Thread-Dump?

Es ist ein Zeiger im Speicher auf den Thread. Hier ist eine detailliertere Beschreibung:

C.4.1 Thread-Informationen

Der erste Teil des Thread-Abschnitts zeigt den Thread, der den schwerwiegenden Fehler verursacht hat, wie folgt:

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

Der Thread-Zeiger ist der Zeiger auf die interne Java VM-Thread-Struktur. Dies ist im Allgemeinen nicht von Interesse, es sei denn, Sie debuggen eine Live-Java-VM oder eine Kerndatei.

Diese letzte Beschreibung stammt aus: Fehlerbehebungshandbuch für Java SE 6 mit HotSpot VM

Hier noch ein paar Links zu Thread-Dumps:

James Drinkard
quelle
11

Weiter zu @James Drinkards ausgezeichneter Antwort:

Beachten Sie, dass abhängig von der zugrunde liegenden Implementierung der java.lang.Thread.State eines Threads, der in einer nativen Methode blockiert ist, möglicherweise als RUNNABLE, where gemeldet wirdA 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.

Es stellt sich heraus, dass diese Beschreibung auch das Blockieren in einem Betriebssystemaufruf wie einer Abfrage- oder Leseoperation umfasst - vermutlich, weil nicht garantiert werden kann, dass die JVM weiß, wann ein nativer Methodenaufruf auf Betriebssystemebene blockiert wurde.

Viele Diskussionen über JVM-Thread-Dumps, die ich gesehen habe, ignorieren diese Möglichkeit entweder vollständig oder überfliegen sie munter, ohne die Auswirkungen zu berücksichtigen. Nicht zuletzt können Überwachungstools verwirrend melden, dass mehrere solcher Threads "ausgeführt" werden, und darüber hinaus Sie laufen alle zu 100%.

Jeremy
quelle