Сначала напишите: стек потоков должен быть наиболее эффективным средством для определения нефункциональных проблем многопоточных приложений, и можно сказать, что является убийственным оружием. Стеки потоков лучше всего подходят для анализа следующих типов проблем:
Система имеет слишком высокий процессор без причины.
Система приостановлена, нет ответа.
Система работает медленнее и медленнее.
Узкие места производительности (например, неспособность полностью использовать процессор и т. Д.)
Законный тупик, мертвая петля, голод и т. Д.
Система сбой из -за слишком большого количества потоков (например, неспособность создавать потоки и т. Д.).
Как интерпретировать стек потоков
Как показано в следующей программе исходного кода Java:
пакет org.ccgogoing.study.stacktrace;/** * @author: luochong400 * @description: тестовый поток * @date: создать в 2:27 вечера 2017/12/08 */public class mytest {Object obj1 = new Object (); Объект obj2 = new Object (); public void fun1 () {синхронизированный (obj1) {fun2 (); }} public void fun2 () {synchronized (obj2) {while (true) {// Чтобы печатать стек, анализ стека функций не выходит из System.out.print (""); }} public static void main (string [] args) {myTest aa = new myTest (); aa.fun1 (); }}Запустите программу в Idea, затем нажмите клавишу CTRL+Break, чтобы распечатать информацию о стеке потоков следующим образом:
Полный свалку с дампом Java Hotsopot (TM) 64-битный сервер VM (24,79-B02 смешанный режим): «Сервисный поток» DAMEAN PRIO = 6 TID = 0x0000000000C53B000 NID = 0XCA58 Runnable [0x0000000000000000] Java.lang.Thread.State: runnable ”c2 CompillerTharth TID = 0x000000000C516000 NID = 0xD390 В ожидании условия [0x000000000000000] java.lang.thread.state: Runnable "C2 Compilerthread0" Daemon Prio = 10 Tid = 0x000000000C515000 NID = 0xCBAC В ожидании на условиях [0x0000000000000. java.lang.thread.state: Runnable "Monitor Ctrl-Break" Daemon Prio = 6 tid = 0x000000000C514000 NID = 0xd148 Runnable [0x000000000Caee000] java.lang.Thread.State: runnable at java.net.socketStream.socketRead0. Java.net.socketInputStream.read (socketInputStream.java:152) на java.net.socketinputstream.read (socketinputstream.java:122) на sun.nio.cs.streamdecoder.readbytes (Streamdecoder.java:283) по sun.nio.cs.streamdecoder.implread (streamdecoder.java:325) по адресу sun.nio.cs.streamdecoder.read (Streamdecoder.java:177) - Заблокировано <0x00000000D7858B50> (java.io.inputStreamread) по java.io.inputStreamReader.read (inputStreamReader.java:184) по адресу java.io.bufferedreader.fill (BufferedReader.java:154) на java.io.bufferedreader.readline (BufferedReader.java:317) - Заблокировано <0x00000d.readline (AfferedReader. Java.io.InputStreamReader) по адресу java.io.bufferedReader.readline (BufferedReader.java:382) в com.intellij.rt.excution.application.appmainv2 $ 1.run (appmainv2.java Runnable [0x000000000000000000] java.lang.thread.state: runnable "Диспетчер сигналов" Daemon Prio = 10 tid = 0x0000000000c1a8800 nid = 0xd200 В ожидании условия [0x0000000000000000] java.lang.thread.thread.state: runnable "daemonaiz TID = 0x000000000000CACE6000 NID = 0xCD74 в Object.Wait () [0x000000000C13F000] java.lang.Thread.state: ожидание (на мониторе объекта) на java.lang.object.wait (нативный метод) - ожидание на <0x00000000000d7284858> (java.lange.lange.le.le.le.le.le.le.le.le.le.le. java.lang.ref.referencequeue.remove (rewerqueue.java:135) - заблокировано java.lang.ref.finalizer $ finalizerthread.run (finalizer.java:209) "Справочный обработчик" Daemon Prio = 10 tid = 0x000000000000s4800 nid = 0xce34 в объекте java.lang.object.wait (родной метод) - ожидание <0x00000000d7284470> (java.lang.ref.reference $ lock) по адресу java.lang.object.wait (object.java:503) по адресу java.lang.ref.referen <0x00000000D72844470> (a java.lang.ref.reference $ lock) "main" prio = 6 tid = 0x0000000000238e800 nid = 0xc940. org.ccgogoing.study.stacktrace.mytest.fun2 (mytest.java:22) - заблокирован <0x00000000d77d50c8> (java.lang.object) на org.ccgogoing.study.stacktrace.mytest.fun1 (mytest.javaing.15) <0x00000000d77d50b8> (java.lang.object) At org.ccgogoing.study.stacktrace.mytest.main (mytest.java:29) "VM Thread" PRIO = 10 TID = 0x0000000ACE1000 NID = 0XD0A8 RUNNABLE "GC TASCER#0. 0 (PARALLG). TID = 0x00000000000023A4000 NID = 0xD398 Runnable "GC Task Thread#1 (parallelgc)" Prio = 6 tid = 0x00000000023a5800 nid = 0xcc20 Runnable "GC Task Thread#2 (parallelgc)" Prio = 6 tid = 0x0000000023A7000. «Поток задачи GC#3 (parallelgc)" prio = 6 tid = 0x00000000023a9000 nid = 0xd088 runnable ”Периодическая нить виртуальной машины" Prio = 10 Tid = 0x000000000C53F000 NID = 0xc1b4 В ожидании 6376. [0x000000000D7280000, 0x0000000000D7280000, 0x0000000d9b800000, 0x00000000100000000) Eden Space 31744K, 20%. [0x000000000D9680000, 0x0000000D9680000, 0x0000000D9B80000) до пространства 5120K, 0% [0x000000000D9180000, 0x000000000D9180000, 0x0000000D9180000, 0x00000D9680000). [0x000000000085800000, 0x000000000008A980000, 0x0000000D7280000) Объектное пространство 83456K, 0% [0x000000000858800000, 0x0000000085800000, 0x0000008A980000) PSPERMGEN. [0x0000000000806000000, 0x000000081b000000, 0x0000000085800000) Объектное пространство 21504K, 15% [0x000000000806000000, 0x0000000080939290, 0x00000081B00000000). 0x00000000080939290, 0x00000000081B000000)
На выводе из стека мы видим, что существует много фоновых потоков и основных потоков, среди которых только основной поток принадлежит пользовательским потокам Java, а другие автоматически создаются виртуальными машинами. Во время нашего анализа мы заботимся только о пользовательских потоках.
Из основного потока выше, вы можете увидеть вызывающий контекст текущего потока очень интуитивно понятным способом. Значение определенного слоя вызовов одного потока заключается в следующем:
на mytest.fun1 (mytest.java:15) | | | | | | | | | +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Кроме того, в стеке есть заявление:- Заблокирован <0x0000000D77D50B8> (java.lang.object), указывающий, что поток уже имеет блокировку <0x0000000d77d50b8>, а угловые скобки указывают на идентификатор блокировки. Это автоматически генерируется системой. Нам нужно только знать, что стек печатается каждый раз, и один и тот же идентификатор означает одинаковую блокировку. Первая строка каждого стека потоков означает следующее:
"Main" Prio = 1 tid = 0x000000000238e800 nid = 0xc940 Runnable [0x00000000027af000] | | | | | | | | | | | | | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Язык диапазона и исполняющаяся сущность - это виртуальная машина Java, потоки на языке Java работают с локальными потоками в виртуальной машине и на самом деле являются локальными потоками, выполняющими код потока Java.
Интерпретация замков
Судя по стеке потоков выше, прямая информация, содержащаяся в стеке потоков: количество потоков, стек методов, вызываемый каждым потоком, и текущий статус блокировки. Количество потоков может быть подсчитано напрямую; Стек методов, вызванный потоком, от нижней части вверху, означает, какой метод, на котором класс был вызван текущим потоком. И состояние замка кажется немного сложным. Информация, связанная с блокировкой, заключается в следующем:
Когда в потоке принадлежит блокировка, стек потока будет печатать -заблокирован <0x00000000D77D50C8>
Когда поток ждет, чтобы другие потоки отпустили блокировку, стек потоков будет распечатать -ожидая блокировки <0x00000000D77D50C8>
Когда поток владеет блокировкой, но выполняет метод wait () блокировки, сначала стек потока печатает заблокирован, а затем печатает -ожидая на
<0x00000000D77D50C8>
Интерпретация состояния потока
С помощью стека потоков можно проанализировать много типов проблем, а анализ потребления ЦП является важной частью анализа стека потоков;
Потоки в состояниях Timed_waiting и ожидания не должны потреблять процессор. Для потоков в запущенных, мы должны судить, потребляют ли они процессор на основе характера текущего кодекса.
Если это чистый код работы Java, он потребляет процессор.
Если это сеть io, это редко потребляет процессор.
Если это локальный кодекс, вы должны судить на основе характера локального кодекса (локальный стек потоков может быть получен через PSTACK и GSTACK). Если это чистый код работы, он потребляет процессор. Если он приостановлен, он не потребляет процессор. Если это io, он не потребляет много процессора.
Как проанализировать проблемы со стеком потоков
Стеки потоков очень полезны в позиционировании вопросов следующих типов:
Анализ тупика.
Чрезмерный анализ процессора, вызванный кодом Java
Анализ насильственного цикла
Анализ недостаточных ресурсов
Анализ узкого места производительности
Анализ тупика потока
Я не буду слишком много объяснять о концепции тупика. Если вы не понимаете, вы можете проверить это онлайн;
Кольцо блокировки, образованное двумя или более потоками из -за зависимости блокировки петли, образует настоящий тупик, следующим образом:
Нашел один уровень Java тупик: ================================================================ ======================================================================= ======================================================================= ======================================================================= ======================================================================= ======================================================================= ======================================================================= ======================================================================= Bock Monitor 0x000000000000A9ABC78 (Object 0x000000000D77363E0, java.lang.object), который хранится на «org.ccgogoing.study.stacktrace.deadlock.testThread2» Вверху: ======================================================================================== ========================================================================================= ========================================================================================= ========================================================================================= org.ccgogoing.study.stacktrace.deadlock.testThread2 org.ccgogoing.study.stacktrace.deadlock.testThread1 ": at org.ccgogoing.study.stacktrace.deadlock.testThread1.fun (testThread1.java:33) - ждать блокировки <0x00000000d77363e0> (java. <0x00000000D77363D0> (java.lang.object) на org.ccgogoing.study.stacktrace.deadlock.testThread1.run (TestThread1.java:20) Нашел 1 тупик.
Из печатного стека мы можем увидеть «Нашел один тупик на уровне Java:», то есть, если есть ситуация с тупиком, стек напрямую даст результаты анализа тупика.
Когда набор потоков Java умирает, это означает, что игра закончится, эти потоки всегда будут повесить там и никогда не смогут продолжать работать. Когда поток с тупиком выполняет критические функции системы, этот тупик может привести к парализованию всей системы. Чтобы восстановить систему, временный и единственный способ избежать ее - это перезапустить систему. Затем поторопитесь и измените ошибку, которая вызвала этот тупик.
Примечание. Две или более потоков, которые находятся в тупике, не потребляют процессор. Некоторые люди считают, что уровень использования 100% использования процессора вызван тупиком. Это утверждение совершенно неверно. Мертвой петли, и код в цикле является процессором, что может привести к 100% частоту использования процессора. Операции ввода -вывода, такие как розетки или базы данных, не потребляют много процессора.