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

100

Я пытаюсь больше узнать о 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 ....) тот же вопрос, что в <..>

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

"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

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

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

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

Спасибо

Леонардо
источник

Ответы:

113

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

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

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

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

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

Затем идет дальше:

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

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

«Поток-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 ()», это означает, что поток демона, здесь «финализатор», ожидает уведомления о блокировке объекта, в этом случае он показывает вам, какое уведомление он ожидает: «- ожидает <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. Обычно это не представляет интереса, если вы не отлаживаете живую виртуальную машину Java или файл ядра.

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

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

Джеймс Дринкард
источник
11

В дополнение к отличному ответу @James Дринкарда:

Обратите внимание, что, в зависимости от базовой реализации, 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%.

Джереми
источник