Jak analizować zrzut wątku Java?

100

Próbuję dowiedzieć się więcej o Javie, zwłaszcza o zarządzaniu pamięcią i wątkach. Z tego powodu ostatnio zainteresowałem się przeglądaniem zrzutów nici.

Oto kilka wierszy pobranych z aplikacji internetowej przy użyciu VisualVM, wbudowanego narzędzia dla języka 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)

Najpierw mam pytania dotyczące niektórych nazw zmiennych:

  • co znaczy tid and nid?
  • Jaka jest liczba w nawiasach kwadratowych po Object.wait?

Następnie dla samego śladu stosu:

  • co to znaczy czekać na <.....> (java.lang ....) i jaka jest liczba w <..>
  • co to znaczy zablokowane <.....> (java.lang ....) to samo pytanie, co jest w <..>

Myślałem, że słowo zablokowane było w jakiś sposób powiązane z warunkiem oczekiwania, jednak się myliłem. W rzeczywistości zastanawiam się, dlaczego zablokowany jest powtarzany trzy razy, ale wątek jest w stanie gotowym do uruchomienia, jak widać na tym samym zrzucie:

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

Na koniec to był najgorszy z nich:

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

Ten wątek jest gotowy do uruchomienia, ale czeka pod warunkiem. Jaki stan i co to jest 0x00000?

Dlaczego ślad stosu jest tak krótki bez żadnego dowodu na klasę wątku?

Byłbym bardzo wdzięczny, gdybyś mógł odpowiedzieć na wszystkie moje pytania.

Dzięki

Leonardo
źródło

Odpowiedzi:

113

TID to thead id, a NID to: Native wątku ID. Ten identyfikator jest w dużym stopniu zależny od platformy. To NID w zrzutach wątków jstack. W systemie Windows jest to po prostu identyfikator wątku na poziomie systemu operacyjnego w procesie. W Linuksie i Solarisie jest to PID wątku (co z kolei jest lekkim procesem). W systemie Mac OS X mówi się, że jest to natywna wartość pthread_t.

Przejdź do tego linku: Identyfikator wątku na poziomie Java : w celu uzyskania definicji i dalszego wyjaśnienia tych dwóch terminów.

Na stronie IBM znalazłem takie łącze: Jak interpretować zrzut wątku . który omawia to bardziej szczegółowo:

Wyjaśnia, co oznacza czekanie: Blokada uniemożliwia więcej niż jednej jednostce dostęp do współdzielonego zasobu. Każdy obiekt w języku Java ™ ma skojarzoną blokadę (uzyskaną za pomocą zsynchronizowanego bloku lub metody). W przypadku maszyny JVM wątki konkurują ze sobą o różne zasoby w JVM i blokują obiekty Java.

Następnie opisuje monitor jako specjalny rodzaj mechanizmu blokującego, który jest używany w JVM w celu umożliwienia elastycznej synchronizacji między wątkami. Na potrzeby tej sekcji terminy monitoruj i blokuj zamiennie.

Potem idzie dalej:

Aby uniknąć posiadania monitora na każdym obiekcie, maszyna JVM zwykle używa flagi w bloku klasy lub metody w celu wskazania, że ​​element jest zablokowany. W większości przypadków fragment kodu przechodzi przez jakąś zamkniętą sekcję bez rywalizacji. Dlatego flaga strażnika wystarczy, aby zabezpieczyć ten fragment kodu. Nazywa się to płaskim monitorem. Jeśli jednak inny wątek chce uzyskać dostęp do jakiegoś zablokowanego kodu, doszło do prawdziwej rywalizacji. JVM musi teraz utworzyć (lub nadmuchać) obiekt monitora do przechowywania drugiego wątku i zorganizowania mechanizmu sygnalizacyjnego w celu koordynowania dostępu do sekcji kodu. Ten monitor jest teraz nazywany monitorem napompowanym.

Oto bardziej szczegółowe wyjaśnienie tego, co widzisz na liniach ze zrzutu wątku. Wątek Java jest implementowany przez natywny wątek systemu operacyjnego. Każdy wątek jest reprezentowany przez pogrubioną linię, na przykład:

„Wątek-1” (TID: 0x9017A0, sys_thread_t: 0x23EAC8, stan: R, natywny identyfikator: 0x6E4) prio = 5

* Poniższe 6 pozycji wyjaśnia to, ponieważ dopasowałem je z przykładu, wartości w nawiasach []:

  1. nazwa [ Wątek-1 ],
  2. identyfikator [ 0x9017A0 ],
  3. Adres struktury danych JVM [ 0x23EAC8 ],
  4. stan obecny [ R ],
  5. natywny identyfikator wątku [ 0x6E4 ],
  6. i priorytet [ 5 ].

„Czekaj na” wydaje się być wątkiem demona powiązanym z samą jvm, a nie wątkiem aplikacji perse. Kiedy pojawi się „in Object.wait ()”, oznacza to, że wątek demona, tutaj „finalizer”, czeka na powiadomienie o blokadzie obiektu, w tym przypadku pokazuje, na które powiadomienie oczekuje: „- czekam na <0x27ef0288> (java.lang.ref.ReferenceQueue $ Lock) ”

Definicja ReferenceQueue jest następująca: Kolejki referencyjne, do których zarejestrowane obiekty referencyjne są dołączane przez moduł odśmiecania pamięci po wykryciu odpowiednich zmian osiągalności.

Wątek finalizatora działa, więc wyrzucanie elementów bezużytecznych działa w celu czyszczenia zasobów skojarzonych z obiektem. Jeśli widzę to poprawnie, finalizator nie może uzyskać blokady tego obiektu: java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:118), ponieważ obiekt java uruchamia metodę, więc wątek finalizatora jest zablokowany, dopóki ten obiekt nie zakończy bieżącego zadania.

Ponadto finalizator nie tylko chce odzyskać pamięć, ale jest bardziej zaangażowany niż czyszczenie zasobów. Muszę się więcej nad tym przestudiować, ale jeśli masz otwarte pliki, gniazda itp. Związane z metodami obiektów, finalizator będzie również pracował nad zwolnieniem tych elementów.

Jaka jest liczba w nawiasach kwadratowych po Object.wait w zrzucie wątku?

Jest to wskaźnik w pamięci do wątku. Oto bardziej szczegółowy opis:

C.4.1 Informacje o wątku

Pierwsza część sekcji wątku przedstawia wątek, który spowodował błąd krytyczny, w następujący sposób:

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

Wskaźnik wątku jest wskaźnikiem do wewnętrznej struktury wątków maszyny wirtualnej Java. Na ogół nie jest to interesujące, chyba że debugujesz działającą maszynę wirtualną Java lub plik podstawowy.

Ten ostatni opis pochodzi z: Podręcznika rozwiązywania problemów dla Java SE 6 z maszyną wirtualną HotSpot

Oto kilka dodatkowych linków do zrzutów wątków:

Jamesa Drinkarda
źródło
11

W nawiązaniu do doskonałej odpowiedzi @Jamesa Drinkarda:

Należy zauważyć, że w zależności od podstawowej implementacji java.lang.Thread.State wątku, który jest blokowany w metodzie natywnej, może być raportowany jako RUNNABLE, gdzieA 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.

Okazuje się, że opis ten obejmuje również blokowanie w wywołaniu systemu operacyjnego, takim jak sondowanie lub operacja odczytu - prawdopodobnie dlatego, że nie ma gwarancji, że JVM może wiedzieć, kiedy wywołanie metody natywnej zostało zablokowane na poziomie systemu operacyjnego.

Wiele dyskusji na temat zrzutów wątków JVM, które widziałem, albo całkowicie ignoruje tę możliwość, albo beztrosko przegląda ją bez rozważania konsekwencji - z których najważniejszym jest to, że narzędzia do monitorowania mogą myląco zgłaszać, że kilka takich wątków `` działa '', a ponadto wszystkie działają na 100%.

Jeremy
źródło