Як проаналізувати скидання 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)

Спочатку у мене є запитання щодо деяких змінних імен:

  • що значить прилив і нід?
  • Яка цифра в квадратних дужках після 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 - це тед-ідентифікатор, а NID - ідентифікатор власного потоку. Цей ідентифікатор залежить від платформи. Це NID в скидах для потоків jstack. У Windows це просто ідентифікатор потоку на рівні OS в процесі. Для Linux і Solaris - це PID потоку (що, в свою чергу, є легким процесом). У Mac OS X, як кажуть, це нативне значення pthread_t.

Перейдіть за цим посиланням: Ідентифікатор потоку на рівні 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, а не переслідуванням потоку програми. Коли ви отримуєте "в Object.wait ()", це означає, що демонова нитка "фіналізатор" тут чекає на сповіщення про блокування об'єкта, в цьому випадку воно показує, яке сповіщення його чекає: "- чекає на <0x27ef0288> (a java.lang.ref.ReferenceQueue $ Lock) "

Визначення черги ReferenceQue: Довідкові черги, до яких зареєстровані опорні об'єкти додаються сміттєзбірником після виявлення відповідних змін доступності.

Нитка фіналізатора працює так, що збирання сміття працює для очищення ресурсів, пов'язаних з об'єктом. Якщо я бачу це прямо, фіналізатор не може отримати блокування для цього об’єкта: 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 VM або основний файл.

Цей останній опис з'явився з: Посібник з усунення несправностей для Java SE 6 з HotSpot VM

Ось ще кілька посилань на скиди ниток:


11

Далі до чудової відповіді @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%.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.