Что такое дамп потока и как его анализировать?

Что такое дамп потока и как его анализировать?

Поговорим о дампе потока и о том, как его анализировать.

Мы также обсудим, как это помогает выявлять проблемы, и некоторые из анализаторов, которые вы можете использовать.

Что такое нить?

Процесс — это компьютерная программа, которая загружается в память компьютера и выполняется. Он может выполняться процессором или набором процессоров. Процесс описывается в памяти с важной информацией, такой как хранилища переменных, дескрипторы файлов, счетчик команд, регистры, сигналы и т. д.

Процесс может состоять из множества легковесных процессов, называемых потоками. Это помогает достичь параллелизма, когда процесс разбит на несколько потоков. Это приводит к лучшей производительности. Все потоки внутри процесса используют одно и то же пространство памяти и зависят друг от друга.

Дампы потоков

Когда процесс выполняется, мы можем определить текущее состояние выполнения потоков в процессе, используя дампы потоков. Дамп потока содержит моментальный снимок всех потоков, активных в определенное время во время выполнения программы. Содержит всю необходимую информацию о потоке и его текущем состоянии.

Современное приложение сегодня включает несколько номеров потоков. Каждый поток требует определенных ресурсов, выполняет определенные действия, связанные с процессом. Это может повысить производительность приложения, поскольку потоки не могут использовать доступные ядра ЦП.

Но есть компромиссы, т.е. иногда несколько потоков могут плохо координироваться друг с другом, что может привести к взаимоблокировкам. Поэтому, если что-то пойдет не так, мы можем использовать дампы потоков для проверки состояния наших потоков.

Дамп потоков в Java

Дамп потока JVM — это список состояний всех потоков, которые являются частью процесса в данный конкретный момент времени. Содержит информацию о стеке потока, представленную в виде трассировки стека. Поскольку он написан простым текстом, его содержимое можно сохранить для последующего просмотра. Анализ дампов потоков может помочь

  • Оптимизация производительности JVM
  • Оптимизация производительности приложений
  • Диагностика проблем, т.е. взаимоблокировки, конфликты потоков и т. д.

Генерация дампов потоков

Существует множество способов создания дампов потоков. Ниже приведены некоторые инструменты на основе JVM, которые можно запустить из командной строки/терминала (инструменты CLI) или из каталога /bin (инструменты с графическим интерфейсом) каталога установки Java.

Давайте исследуем их.

№1. jStack

Самый простой способ создать дамп потока — использовать jStack. jStack поставляется с JVM и может использоваться из командной строки. Здесь нам нужен PID процесса, для которого мы хотим сгенерировать дамп потока. Чтобы получить PID, мы можем использовать команду jps, как показано ниже.

jps -l

jps перечисляет все идентификаторы процессов Java.

В Windows

C:Program FilesJavajdk1.8.0_171bin>jps -l
47172 portal
6120 sun.tools.jps.Jps
C:Program FilesJavajdk1.8.0_171bin>

В Linux

[[email protected] ~]# jps -l
1088 /opt/keycloak/jboss-modules.jar
26680 /var/lib/jenkins/workspace/kyc/kyc/target/kyc-1.0.jar
7193 jdk.jcmd/sun.tools.jps.Jps
2058 /usr/share/jenkins/jenkins.war
11933 /var/lib/jenkins/workspace/admin-portal/target/portal-1.0.jar
[[email protected] ~]#

Как мы видим здесь, мы получаем список всех запущенных java-процессов. Содержит локальный идентификатор виртуальной машины для запущенного процесса Java и имя приложения в столбцах один и два. Теперь для создания потоков дампа мы используем программу jStack с флагом –l, которая создает длинный список вывода дампа. Мы также можем отправить вывод в текстовый файл по нашему выбору.

jstack -l 26680

[[email protected] ~]# jstack -l 26680
2020-06-27 06:04:53
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):

"Attach Listener" #16287 daemon prio=9 os_prio=0 tid=0x00007f0814001800 nid=0x4ff2 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"logback-8" #2316 daemon prio=5 os_prio=0 tid=0x00007f07e0033000 nid=0x4792 waiting on condition [0x00007f07baff8000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for   (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

"logback-7" #2315 daemon prio=5 os_prio=0 tid=0x00007f07e0251800 nid=0x4791 waiting on condition [0x00007f07bb0f9000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for   (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

№ 2. jvisualvm

Jvisualvm — это инструмент с графическим интерфейсом, который помогает нам устранять неполадки, отслеживать и профилировать приложения Java. Он также поставляется с JVM и может быть запущен из каталога /bin нашей установки java. Он очень интуитивно понятен и прост в использовании. Помимо других опций, это также позволяет нам захватить дамп потока для определенного процесса.

Чтобы увидеть дамп потока для определенного процесса, мы можем щелкнуть правой кнопкой мыши программу и выбрать «Дамп потока» в контекстном меню.

№3. jcmd

JCMD — это утилита командной строки, которая поставляется с JDK и используется для отправки запросов диагностических команд в JVM.

Однако он работает только на локальном компьютере, на котором запущено приложение Java. Его можно использовать для управления записью полета Java, диагностики и устранения неполадок с приложениями JVM и Java. Мы можем использовать команду Thread.print из jcmd, чтобы получить список дампов потоков для конкретного процесса, указанного в PID.

Ниже приведен пример того, как мы можем использовать jcmd.

jcmd 28036 Thread.print

C:Program FilesJavajdk1.8.0_171bin>jcmd 28036 Thread.print
28036:
2020-06-27 21:20:02
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode):

"Bundle File Closer" #14 daemon prio=5 os_prio=0 tid=0x0000000021d1c000 nid=0x1d4c in Object.wait() [0x00000000244ef000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Unknown Source)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextEvent(EventManager.java:403)
        - locked  (a org.eclipse.osgi.framework.eventmgr.EventManager$EventThread)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:339)

"Active Thread: Equinox Container: 0b6cc851-96cd-46de-a92b-253c7f7671b9" #12 prio=5 os_prio=0 tid=0x0000000022e61800 nid=0xbff4 waiting on condition [0x00000000243ee000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for   (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(Unknown Source)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000021a7b000 nid=0x2184 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #9 daemon prio=9 os_prio=2 tid=0x00000000219f5000 nid=0x1300 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x00000000219e0000 nid=0x48f4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x00000000219df000 nid=0xb314 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x00000000219db800 nid=0x2260 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x00000000219d9000 nid=0x125c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000219d8000 nid=0x834 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001faf3000 nid=0x36c0 in Object.wait() [0x0000000021eae000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on  (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(Unknown Source)
        - locked  (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(Unknown Source)
        at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000005806000 nid=0x13c0 in Object.wait() [0x00000000219af000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on  (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Unknown Source)
        at java.lang.ref.Reference.tryHandlePending(Unknown Source)
        - locked  (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)

"main" #1 prio=5 os_prio=0 tid=0x000000000570e800 nid=0xbf8 runnable [0x0000000000fec000]
   java.lang.Thread.State: RUNNABLE
        at java.util.zip.ZipFile.open(Native Method)
        at java.util.zip.ZipFile.(Unknown Source)
        at java.util.zip.ZipFile.(Unknown Source)
        at java.util.zip.ZipFile.(Unknown Source)
        at org.eclipse.osgi.framework.util.SecureAction.getZipFile(SecureAction.java:307)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.getZipFile(ZipBundleFile.java:136)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.lockOpen(ZipBundleFile.java:83)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.getEntry(ZipBundleFile.java:290)
        at org.eclipse.equinox.weaving.hooks.WeavingBundleFile.getEntry(WeavingBundleFile.java:65)
        at org.eclipse.osgi.storage.bundlefile.BundleFileWrapper.getEntry(BundleFileWrapper.java:55)
        at org.eclipse.osgi.storage.BundleInfo$Generation.getRawHeaders(BundleInfo.java:130)
        - locked  (a java.lang.Object)
        at org.eclipse.osgi.storage.BundleInfo$CachedManifest.get(BundleInfo.java:599)
        at org.eclipse.osgi.storage.BundleInfo$CachedManifest.get(BundleInfo.java:1)
        at org.eclipse.equinox.weaving.hooks.SupplementerRegistry.addSupplementer(SupplementerRegistry.java:172)
        at org.eclipse.equinox.weaving.hooks.WeavingHook.initialize(WeavingHook.java:138)
        at org.eclipse.equinox.weaving.hooks.WeavingHook.start(WeavingHook.java:208)
        at org.eclipse.osgi.storage.FrameworkExtensionInstaller.startActivator(FrameworkExtensionInstaller.java:261)
        at org.eclipse.osgi.storage.FrameworkExtensionInstaller.startExtensionActivators(FrameworkExtensionInstaller.java:198)
        at org.eclipse.osgi.internal.framework.SystemBundleActivator.start(SystemBundleActivator.java:112)
        at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:815)
        at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:808)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:765)
        at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:1005)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule.initWorker(EquinoxBundle.java:190)
        at org.eclipse.osgi.container.SystemModule.init(SystemModule.java:99)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle.init(EquinoxBundle.java:272)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle.init(EquinoxBundle.java:257)
        at org.eclipse.osgi.launch.Equinox.init(Equinox.java:171)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.startup(EclipseStarter.java:316)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:251)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:661)
        at org.eclipse.equinox.launcher.Main.basicRun(Main.java:597)
        at org.eclipse.equinox.launcher.Main.run(Main.java:1476)

"VM Thread" os_prio=2 tid=0x000000001fae8800 nid=0x32cc runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000005727800 nid=0x3264 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000005729000 nid=0xbdf4 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000572a800 nid=0xae6c runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000572d000 nid=0x588 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000000000572f000 nid=0xac0 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000005730800 nid=0x380 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000005733800 nid=0x216c runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000005734800 nid=0xb930 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000021a8d000 nid=0x2dcc waiting on condition

JNI global references: 14


C:Program FilesJavajdk1.8.0_171bin>

№ 4. СМК

JMC расшифровывается как Java Mission Control. Это инструмент с графическим интерфейсом с открытым исходным кодом, который поставляется с JDK и используется для сбора и анализа данных приложений Java.

Его можно запустить из папки /bin нашей установки Java. Администраторы и разработчики Java используют этот инструмент для сбора подробной низкоуровневой информации о JVM и поведении приложений. Обеспечивает подробный и эффективный анализ данных, собранных Java Flight Recorder.

Когда мы запускаем jmc, мы видим список java-процессов, запущенных на локальной машине. Также возможно удаленное подключение. В конкретном процессе мы можем щелкнуть правой кнопкой мыши и выбрать «Начать запись полета», а затем проверить дампы потоков на вкладке «Потоки».

№ 5. jconsole

jconsole — это инструмент расширения управления Java, используемый для управления жалобами и мониторинга.

Он также имеет набор предопределенных операций с агентом JMKS, которые может выполнять пользователь. Это позволяет пользователю обнаруживать и анализировать трассировку стека программы в реальном времени. Его можно запустить из папки /bin нашей установки Java.

Используя инструмент jconsole GUI, мы можем просмотреть трассировку стека каждого потока, когда мы подключаем его к работающему процессу Java. Затем на вкладке Thread мы можем увидеть имена всех запущенных потоков. Чтобы обнаружить взаимоблокировку, мы можем нажать «Обнаружить взаимоблокировку» в правом нижнем углу окна. Если обнаружена взаимоблокировка, она появится на новой вкладке, в противном случае будет отображаться Нет взаимоблокировки.

№ 6. ThreadMcBean

ThreadMKSBean — это интерфейс управления потоками виртуальной машины Java, принадлежащий пакету java.lang.Management. Он в основном используется для обнаружения потоков, которые вошли в ситуацию взаимоблокировки, и получения подробной информации о них.

Мы можем использовать интерфейс ThreadMkBean для программного захвата дампа потока. Метод getThreadMKSBean() объекта ManagementFactory используется для получения экземпляра интерфейса ThreadMKSBean. Возвращает количество активных потоков демона и не-демона. ManagementFactory — это фабричный класс для получения управляемых компонентов для платформы Java.

private static String getThreadDump (boolean lockMonitors, boolean lockSynchronizers) {
    StringBuffer threadDump = new StringBuffer (System.lineSeparator ());
    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean ();
    for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads (lockMonitors, lockSynchronizers)) {
        threadDump.append (threadInfo.toString ());
    }
    return threadDump.toString ();
}

Ручной анализ дампов потоков

Анализ дампов потоков может быть очень полезен для выявления проблем в многопоточных процессах. Такие проблемы, как взаимоблокировки, конфликты между блокировками и чрезмерная загрузка ЦП дампами отдельных потоков, можно решить путем визуализации состояния дампов отдельных потоков.

Максимальная пропускная способность приложения может быть достигнута путем исправления состояния каждого потока после анализа дампа потока.

Например, если процесс использует много ЦП, мы можем узнать, использует ли какой-либо поток больше всего ЦП. Если такой поток существует, мы преобразуем его номер LVP в шестнадцатеричное число. Затем из дампа потока мы можем найти поток с nid, равным ранее полученному шестнадцатеричному номеру. Используя трассировку стека потока, мы можем точно определить проблему. Давайте узнаем идентификатор процесса потока, используя приведенную ниже команду.

ps -mo pid,lvp,stime,time,cpu -C java

[[email protected] ~]# ps -mo pid,lwp,stime,time,cpu -C java
       PID        LWP         STIME           TIME              %CPU
26680               -         Dec07          00:02:02           99.5
         -       10039        Dec07          00:00:00           0.1
         -       10040        Dec07          00:00:00           95.5

Давайте посмотрим ниже дамп потока. Чтобы получить дамп потока для процесса 26680, используйте jstack -l 26680

[[email protected] ~]# jstack -l 26680
2020-06-27 09:01:29
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):

"Attach Listener" #16287 daemon prio=9 os_prio=0 tid=0x00007f0814001800 nid=0x4ff2 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

.
.
.
.
.
.
.
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f085814a000 nid=0x6840 in Object.wait() [0x00007f083b2f1000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked  (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

   Locked ownable synchronizers:
        - None

"VM Thread" os_prio=0 tid=0x00007f0858140800 nid=0x683f runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f0858021000 nid=0x683b runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f0858022800 nid=0x683c runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f0858024800 nid=0x683d runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f0858026000 nid=0x683e runnable

"VM Periodic Task Thread" os_prio=0 tid=0x00007f08581a0000 nid=0x6847 waiting on condition

JNI global references: 1553

Теперь давайте посмотрим, что мы можем исследовать с помощью дампов потоков. Если мы посмотрим на дамп потока, мы увидим много контента, который может быть ошеломляющим. Однако, если мы будем идти шаг за шагом, это может быть довольно просто понять. Давайте разберемся с первой строкой

2020-06-27 09:01:29
Полный дамп потока 64-разрядной виртуальной машины сервера Java HotSpot(TM) (смешанный режим 25.221-b11):

Выше показано время создания дампа и информация об использованной JVM. Далее, наконец, мы можем увидеть список потоков, первым среди них является наш поток ReferenceHandler.

Анализ заблокированных тем

Если мы проанализируем приведенные ниже журналы дампа потоков, мы обнаружим, что он обнаружил потоки со статусом BLOCKED, что сильно снижает производительность приложения. Поэтому, если мы сможем найти заблокированные потоки, мы можем попытаться извлечь потоки, связанные с блокировками, которые потоки пытаются получить. Анализ трассировки стека из потока, в настоящее время удерживающего блокировку, может помочь решить проблему.

[[email protected] ~]# jstack -l 26680
.
.
.
.
" DB-Processor-13" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f000]
java.lang.Thread.State: BLOCKED (on object monitor)
                at beans.ConnectionPool.getConnection(ConnectionPool.java:102)
                - waiting to lock  (a beans.ConnectionPool)
                at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111)
                at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43)
"DB-Processor-14" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f020]
java.lang.Thread.State: BLOCKED (on object monitor)
                at beans.ConnectionPool.getConnection(ConnectionPool.java:102)
                - waiting to lock  (a beans.ConnectionPool)
                at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111)
                at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43)
.
.
.
.

Анализ взаимоблокировки

Еще одним очень распространенным применением дампов потоков является обнаружение взаимоблокировок. Обнаружение и устранение взаимоблокировок может быть намного проще, если мы проанализируем дампы потоков.

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

Следовательно, ни один поток не может продолжать выполнение, что приводит к взаимоблокировке и зависанию приложения. Если дреды присутствуют, то последняя часть дампа потока будет печатать информацию о взаимоблокировке следующим образом.

"Thread-0":
waiting to lock monitor 0x00000250e4982480 (object 0x00000000894465b0, a java.lang.Object),
which is held by "Thread-1"
"Thread-1":
waiting to lock monitor 0x00000250e4982380 (object 0x00000000894465a0, a java.lang.Object),
which is held by "Thread-0"
.
.
.
"Thread-0":
at DeadlockedProgram$DeadlockedRunnableImplementation.run(DeadlockedProgram.java:34)
- waiting to lock  (a java.lang.Object)
- locked  (a java.lang.Object)
at java.lang.Thread.run([email protected]/Thread.java:844)
"Thread-1":
at DeadlockedProgram $DeadlockRunnableImplementation.run(DeadlockedProgram.java:34)
- waiting to lock  (a java.lang.Object)
- locked  (a java.lang.Object)
at java.lang.Thread.run([email protected]/Thread.java:844)

Здесь мы можем увидеть информацию о простоях в достаточно понятном формате.

Кроме того, если обобщить всю вышеприведенную часть дампа потока, то в нем приведена информация ниже.

  • Обработчик ссылок — это удобочитаемое имя потока.
  • #2 — это уникальный идентификатор потока.
  • daemon указывает, является ли поток потоком демона.
  • Числовой приоритет потока задается prio=10.
  • Текущее состояние потока указывается ожиданием условия.
  • Далее мы видим трассировку стека, которая включает информацию о блокировках.

Анализаторы дампа потока

В дополнение к ручному анализу доступны многочисленные инструменты для анализа дампа потока, как в режиме онлайн, так и в автономном режиме. Ниже приведены некоторые из упомянутых инструментов, которые мы можем использовать в зависимости от запроса.

Во-первых, давайте изучим онлайн-инструменты.

№1. Быстрая нить

Fast Thread — это любимый инструмент инженеров DevOps для анализа дампа потока для решения сложных производственных задач. Это онлайн-анализатор дампа потока Java, мы можем загрузить дамп потока в виде файла или напрямую скопировать и вставить дамп потока.

В зависимости от размера он проанализирует дамп потока и отобразит информацию, как показано на снимке экрана.

Характеристики

  • Устранение сбоев JVM, замедлений, утечек памяти, зависаний, всплесков ЦП
  • Мгновенный RCA (не ждите розничных продавцов)
  • Интуитивная панель управления
  • Поддержка REST-API
  • Машинное обучение

№ 2. Анализатор дампа потока Spotify

Spotify Thread Dump Analyzer распространяется под лицензией Apache License версии 2.0. Это онлайн-инструмент, который принимает дамп потока в виде файла, или мы можем напрямую скопировать и вставить дамп потока. В зависимости от размера он проанализирует дамп потока и отобразит информацию, как показано на снимке экрана.

№3. Обзор Jstack

Jstack.review анализирует дампы потоков Java из браузеров. Эта страница предназначена только для клиента.

№ 4. Сайт 24×7

Этот инструмент необходим для обнаружения ошибочных потоков, снижающих производительность виртуальной машины Java (JVM). Такие проблемы, как взаимоблокировки, конфликты между блокировками и чрезмерная загрузка ЦП дампами отдельных потоков, можно решить путем визуализации состояния дампов отдельных потоков.

Максимальная пропускная способность приложения может быть достигнута путем корректировки состояния каждого потока, предоставленного инструментом.

Теперь давайте рассмотрим автономные инструменты.

Когда дело доходит до профилирования, подойдет только лучший инструмент.

№1. JProfiler

JProfiler — один из самых популярных среди Java-разработчиков анализаторов дампов потоков. Интуитивно понятный пользовательский интерфейс JProfiler помогает устранять узкие места в производительности, обнаруживать утечки памяти и понимать проблемы с многопоточностью.

JProfiler поддерживает профилирование на следующих платформах:

  • Окна
  • macOS
  • линукс
  • FreeBSD
  • Солярис
  • AIX
  • HP-УКС

Ниже приведены некоторые функции, которые делают JProfiler лучшим выбором для профилирования наших приложений на JVM.

Характеристики

  • Он поддерживает профилирование базы данных для JDBC, JPA и NoSKL.
  • Также доступна поддержка версии Java Enterprise.
  • Представляет высокоуровневую информацию о вызовах RMI
  • Звездный анализ утечки памяти
  • Широкие возможности КА
  • Встроенный профилировщик потоков тесно интегрирован с представлениями профилирования ЦП.
  • Поддержка платформ, IDE и серверов приложений.

№ 2. IBM ТМДА

IBM Thread and Monitor Dump Analyzer for Java (TMDA) — это инструмент, позволяющий выявлять задержки, взаимоблокировки, конфликты ресурсов и узкие места в дампах потоков Java. Это продукт IBM, но инструмент TMDA предоставляется без каких-либо гарантий или поддержки; однако они пытаются исправить и улучшить инструмент с течением времени.

№3. Управление двигателем

Диспетчер приложений ManageEngine может помочь в мониторинге кучи JVM и памяти, не относящейся к куче. Мы даже можем настроить пороговые значения и получать оповещения по электронной почте, SMS и т. д., а также обеспечить правильную настройку приложения Java.

№ 4. Ваш комплект

YourKit состоит из перечисленных ниже продуктов, именуемых комплектом.

  • Java Profiler — полнофункциональный разработчик с низкой нагрузкой для платформ Java EE и Java SE.
  • YouMonitor — мониторинг производительности и профилирование Jenkins, TeamCiti, Gradle, Maven, Ant, JUnit и TestNG.
  • .NET Profiler — простой в использовании профилировщик производительности и памяти для платформы .NET.

Заключение

Теперь вы знаете, насколько полезны дампы потоков для понимания и диагностики проблем в многопоточных приложениях. Обладая надлежащими знаниями о дампах потоков — их структуре, содержащейся в них информации и т. д., — мы можем использовать их для быстрого выявления причин проблем.

Поделиться в соцсетях