Come analizzare un dump di un thread java?


100

Sto cercando di capire di più su Java, in particolare sulla gestione della memoria e sui thread. Per questo motivo ho recentemente trovato interesse a guardare i dump dei thread.

Ecco alcune righe tratte da un'app Web che utilizza VisualVM, uno strumento integrato per 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)

Per prima cosa ho domande su alcuni nomi di variabili:

  • cosa significano tid e nid?
  • Qual è la cifra tra parentesi al quadrato dopo Object.wait?

Quindi per la traccia dello stack stessa:

  • cosa significa aspettare <.....> (un java.lang ....) e qual è il numero in <..>
  • cosa significa bloccato <.....> (un java.lang ....) stessa domanda, cosa c'è in <..>

Pensavo che la parola bloccato fosse in qualche modo correlata a una condizione di attesa, tuttavia mi sbagliavo. In effetti, mi chiedo perché bloccato viene ripetuto tre volte, ma il thread è in uno stato eseguibile come visto nello stesso 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)

Infine, questo è stato il peggiore di loro:

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

Questo thread è in stato eseguibile, ma è in attesa a condizione. Quale condizione e cos'è 0x00000?

Perché la traccia dello stack è così breve senza alcuna prova della classe thread?

Se potessi rispondere a tutte le mie domande te ne sarei molto grato.

Grazie

Risposte:


113

Il TID è l'id principale e il NID è: ID thread nativo. Questo ID dipende fortemente dalla piattaforma. È il NID nei dump dei thread jstack. Su Windows, è semplicemente l'ID thread a livello di sistema operativo all'interno di un processo. Su Linux e Solaris, è il PID del thread (che a sua volta è un processo leggero). Su Mac OS X, si dice che sia il valore pthread_t nativo.

Vai a questo collegamento: ID thread a livello di Java : per una definizione e un'ulteriore spiegazione di questi due termini.

Sul sito di IBM ho trovato questo collegamento: Come interpretare un dump del thread . che copre questo in maggior dettaglio:

Spiega cosa significa l'attesa: un blocco impedisce a più entità di accedere a una risorsa condivisa. Ogni oggetto in Java ™ ha un blocco associato (ottenuto utilizzando un blocco o un metodo sincronizzato). Nel caso della JVM, i thread competono per varie risorse nella JVM e si bloccano sugli oggetti Java.

Quindi descrive il monitor come un tipo speciale di meccanismo di blocco utilizzato nella JVM per consentire la sincronizzazione flessibile tra i thread. Ai fini di questa sezione, leggere i termini monitorare e bloccare in modo intercambiabile.

Poi va oltre:

Per evitare di avere un monitor su ogni oggetto, la JVM di solito utilizza un flag in un blocco di classe o metodo per indicare che l'elemento è bloccato. La maggior parte delle volte, un pezzo di codice transiterà in una sezione bloccata senza conflitti. Pertanto, la bandiera del guardiano è sufficiente per proteggere questo pezzo di codice. Questo è chiamato monitor piatto. Tuttavia, se un altro thread desidera accedere a un codice bloccato, si è verificato un vero conflitto. La JVM deve ora creare (o gonfiare) l'oggetto monitor per contenere il secondo thread e predisporre un meccanismo di segnalazione per coordinare l'accesso alla sezione del codice. Questo monitor è ora chiamato monitor gonfiato.

Ecco una spiegazione più approfondita di ciò che stai vedendo sulle righe dal dump del thread. Un thread Java è implementato da un thread nativo del sistema operativo. Ogni thread è rappresentato da una linea in grassetto come:

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

* I seguenti 6 elementi spiegano questo dato che li ho abbinati dall'esempio, i valori tra parentesi []:

  1. nome [ Thread-1 ],
  2. identificatore [ 0x9017A0 ],
  3. Indirizzo struttura dati JVM [ 0x23EAC8 ],
  4. stato attuale [ R ],
  5. identificatore thread nativo [ 0x6E4 ],
  6. e priorità [ 5 ].

Il "wait on" sembra essere un thread daemon associato alla jvm stessa e non al thread dell'applicazione perse. Quando ottieni un "in Object.wait ()", significa che il thread del demone, "finalizer" qui, è in attesa di una notifica su un blocco su un oggetto, in questo caso ti mostra quale notifica è in attesa: "- in attesa di <0x27ef0288> (un blocco java.lang.ref.ReferenceQueue $) "

La definizione di ReferenceQueue è: Code di riferimento, alle quali gli oggetti di riferimento registrati vengono aggiunti dal Garbage Collector dopo che sono state rilevate le modifiche di raggiungibilità appropriate.

Il thread del finalizzatore viene eseguito in modo che la garbage collection funzioni per pulire le risorse associate a un oggetto. Se lo vedo correttamente, il finalizzatore non può ottenere il blocco su questo oggetto: java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:118) perché l'oggetto java sta eseguendo un metodo, quindi il thread del finalizzatore è bloccato finché l'oggetto non ha terminato con l'attività corrente.

Inoltre, il finalizzatore non sta solo cercando di recuperare la memoria, è più coinvolto di quello per ripulire le risorse. Devo studiarlo di più, ma se hai file aperti, socket, ecc ... relativi ai metodi di un oggetto, il finalizzatore lavorerà anche per liberare quegli elementi.

Qual è la cifra tra parentesi al quadrato dopo Object.wait nel dump del thread?

È un puntatore in memoria al thread. Ecco una descrizione più dettagliata:

C.4.1 Informazioni sul thread

La prima parte della sezione thread mostra il thread che ha provocato l'errore fatale, come segue:

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

Il puntatore al thread è il puntatore alla struttura del thread interno di Java VM. In genere non è di alcun interesse a meno che non si esegua il debug di una Java VM live o di un file core.

Quest'ultima descrizione proviene da: Guida alla risoluzione dei problemi per Java SE 6 con HotSpot VM

Ecco alcuni altri collegamenti sui dump dei thread:


11

Oltre all'eccellente risposta di @James Drinkard:

Si noti che, a seconda dell'implementazione sottostante, il java.lang.Thread.State di un thread bloccato in un metodo nativo può essere segnalato come RUNNABLE, doveA 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.

Risulta che questa descrizione comprende anche il blocco in una chiamata del sistema operativo come un'operazione di polling o di lettura, presumibilmente perché non vi è alcuna garanzia che la JVM possa sapere quando una chiamata al metodo nativo è stata bloccata a livello di sistema operativo.

Molte discussioni sui dump dei thread JVM che ho visto ignorano completamente questa possibilità o la sfogliano allegramente senza considerare le implicazioni, non ultimo il fatto che gli strumenti di monitoraggio possono riportare in modo confuso che molti di questi thread sono `` in esecuzione '', e inoltre che funzionano tutti al 100%.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.