Как анализировать дамп потока Java?

Я пытаюсь понять больше о java, особенно об управлении памятью и потоках. По этой причине я недавно нашел интерес к просмотру дампов потоков.

Вот несколько строк, взятых из веб-приложения с помощью VisualVM, встроенного инструмента для 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)

Сначала у меня есть вопросы о некоторых именах переменных:

  • Что означает tid и nid?
  • Какова цифра в квадратных скобках после Object.wait?

Затем для самой трассировки стека:

  • что означает ожидание на <..... > (java.lang....) и что число в <.. >
  • что это значит заблокирован <..... > (java.lang....) тот же вопрос, что в <.. >

Я думал, что слово заблокированное связано каким-то образом с условием ожидания, однако я ошибался. На самом деле, мне интересно, почему блокировка повторяется три раза, но поток находится в состоянии runnable, как видно на том же дампе:

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

В последнем случае это было худшим из них:

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

Этот поток находится в состоянии runnable, но он ждет при условии. Какое состояние и что такое 0x00000?

Почему трассировка стека настолько коротка без каких-либо доказательств класса потока?

Если бы вы могли ответить на все мои вопросы, я был бы очень благодарен.

Спасибо

Ответ 1

TID - это идентификатор идентификатора, а NID - это: собственный идентификатор потока. Этот идентификатор сильно зависит от платформы. Это NID в дампе потока jstack. В Windows это просто идентификатор потока на уровне ОС внутри процесса. В Linux и Solaris это PID потока (что, в свою очередь, является легковесным процессом). В Mac OS X это называется родным значением pthread_t.

Перейдите по этой ссылке: Идентификатор потока уровня Java: для определения и дальнейшего объяснения этих двух терминов.

На сайте IBM я нашел эту ссылку: Как интерпретировать дамп потока. это охватывает это более подробно:

Это объясняет, что означает ожидание: блокировка не позволяет нескольким объектам получить доступ к общему ресурсу. Каждый объект в Java ™ имеет связанную блокировку (полученную с помощью синхронизированного блока или метода). В случае JVM потоки конкурируют за различные ресурсы в JVM и блокируют объекты Java.

Затем он описывает монитор как особый вид механизма блокировки, который используется в JVM для обеспечения гибкой синхронизации между потоками. Для целей этого раздела прочитайте термины "монитор" и "взаимозаменяемо".

Тогда это идет дальше:

Чтобы избежать мониторинга каждого объекта, JVM обычно использует флаг в блоке класса или метода, чтобы указать, что элемент заблокирован. Большую часть времени фрагмент кода будет проходить через некоторые заблокированные разделы без конфликтов. Следовательно, флага-хранителя достаточно, чтобы защитить этот фрагмент кода. Это называется плоский монитор. Однако, если другой поток хочет получить доступ к какому-либо заблокированному коду, произошел настоящий конфликт. JVM должна теперь создать (или раздуть) объект монитора для удержания второго потока и организовать механизм сигнализации для координации доступа к разделу кода. Этот монитор теперь называется надутым монитором.

Вот более подробное объяснение того, что вы видите в строках из дампа потока. Поток Java реализован собственным потоком операционной системы. Каждый поток представлен жирным шрифтом, например:

"Thread-1" (TID: 0x9017A0, sys_thread_t: 0x23EAC8, состояние: R, собственный идентификатор: 0x6E4) prio = 5

* Следующие 6 пунктов объясняют это, поскольку я сопоставил их с примером, значения в скобках []:

  1. имя [ Тема-1 ],
  2. идентификатор [ 0x9017A0 ],
  3. Адрес структуры данных JVM [ 0x23EAC8 ],
  4. текущее состояние [ R ],
  5. собственный идентификатор потока [ 0x6E4 ],
  6. и приоритет [ 5 ].

"Ожидание при включении" представляется потоком демона, связанным с самим jvm, а не с самим потоком приложения. Когда вы получаете "in Object.wait()", это означает, что поток демона, здесь "finalizer", ожидает уведомления о блокировке объекта, в этом случае он показывает, какое уведомление он ожидает: "- ожидание <0x27ef0288> (java.lang.ref.ReferenceQueue $ Lock) "

Определение ReferenceQueue: эталонные очереди, к которым зарегистрированные сборщики объектов добавляются сборщиком мусора после обнаружения соответствующих изменений достижимости.

Выполняется поток финализатора, поэтому сборщик мусора очищает ресурсы, связанные с объектом. Если я вижу это по сути, финализатор не может получить блокировку для этого объекта: java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118), потому что объект java выполняет метод, поэтому поток финализатора заблокирован, пока этот объект не будет завершен с текущей задачей.

Кроме того, финализатор не просто пытается восстановить память, он более сложен, чем очистка ресурсов. Мне нужно больше изучить его, но если у вас есть открытые файлы, сокеты и т.д., Связанные с методами объектов, то финализатор также будет работать над освобождением этих элементов.

Какая цифра в квадратных скобках после Object.wait в дампе потока?

Это указатель в памяти на поток. Вот более подробное описание:

C.4.1 Информация о потоке

Первая часть раздела потока показывает поток, который вызвал фатальную ошибку, следующим образом:

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

Указатель потока - это указатель на структуру внутреннего потока Java VM. Обычно это не представляет интереса, если вы не отлаживаете живую виртуальную машину Java или файл ядра.

Это последнее описание взято из: Руководство по устранению неполадок для Java SE 6 с HotSpot VM

Вот еще несколько ссылок на дампы потоков:

Ответ 2

В дополнение к @James Drinkard отличный ответ:

Обратите внимание, что в зависимости от базовой реализации java.lang.Thread.State потока, который заблокирован в собственном методе, может сообщается как RUNNABLE, где 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.

Оказывается, это описание также включает в себя блокировку в вызове ОС, например, операцию опроса или чтения - по-видимому, потому что нет гарантии, что JVM может знать, когда вызов нативного метода заблокирован на уровне ОС.

Многие обсуждения дампов потоков JVM, которые я видел, либо полностью игнорируют эту возможность, либо блаженно просматривают ее, не учитывая последствий, не в последнюю очередь из того, что инструменты мониторинга могут смутно сообщать, что несколько таких потоков "работают", и, кроме того, все они работают на 100%.

Ответ 3

Попробуйте http://fastthread.io

Это достаточно вероятно для многих случаев.