
スレッド ダンプとその分析方法について説明します。
また、問題を特定するのにどのように役立つか、および使用できるアナライザーの一部についても説明します。
スレッドとは
プロセスとは、コンピュータのメモリにロードされ、実行中のコンピュータ プログラムです。 プロセッサまたはプロセッサのセットによって実行できます。 プロセスは、変数ストア、ファイル ハンドル、プログラム カウンター、レジスタ、シグナルなどの重要な情報と共にメモリに記述されます。
プロセスは、スレッドと呼ばれる多数の軽量プロセスで構成できます。 これは、プロセスが複数のスレッドに分割される並列処理を実現するのに役立ちます。 これにより、パフォーマンスが向上します。 プロセス内のすべてのスレッドは同じメモリ空間を共有し、互いに依存しています。
スレッドダンプ
プロセスが実行されている場合、スレッド ダンプを使用して、プロセス内のスレッドの現在の実行状態を検出できます。 スレッド ダンプには、プログラム実行中の特定の時点でアクティブなすべてのスレッドのスナップショットが含まれています。 スレッドとその現在の状態に関するすべての関連情報が含まれています。
今日の最新のアプリケーションには、複数のスレッドが含まれています。 各スレッドは特定のリソースを必要とし、プロセスに関連する特定のアクティビティを実行します。 これにより、スレッドが使用可能な CPU コアを利用できるため、アプリケーションのパフォーマンスが向上します。
ただし、トレードオフがあります。たとえば、複数のスレッドが互いにうまく連携できず、デッドロック状態が発生することがあります。 そのため、何か問題が発生した場合は、スレッド ダンプを使用してスレッドの状態を調べることができます。
Java のスレッド ダンプ
JVM スレッド ダンプは、特定の時点でプロセスの一部であるすべてのスレッドの状態のリストです。 これには、スタック トレースとして表示されるスレッドのスタックに関する情報が含まれています。 平文で書かれているので、内容を保存して後で見直すことができます。 スレッド ダンプの分析は、
- JVM パフォーマンスの最適化
- アプリケーションのパフォーマンスの最適化
- デッドロック、スレッド競合などの問題の診断
スレッド ダンプの生成
スレッド ダンプを生成するには、さまざまな方法があります。 以下にいくつかの JVM ベースのツールを示します。コマンド ライン/ターミナル (CLI ツール) または Java のインストール フォルダーの /bin (GUI ツール) ディレクトリから実行できます。
それらを調べてみましょう。
#1。 jStack
スレッド ダンプを生成する最も簡単な方法は、jStack を使用することです。 jStack は JVM に同梱されており、コマンド ラインから使用できます。 ここでは、スレッド ダンプを生成するプロセスの PID が必要です。 PID を取得するには、以下に示すように jps コマンドを使用できます。
jps-l
jps は、すべての Java プロセス ID を一覧表示します。
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 プロセスのローカル VM ID とアプリケーションの名前がそれぞれ列 1 と列 2 に含まれています。 ここで、スレッド ダンプを生成するために、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 <0x00000006ca9a1fc0> (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 <0x00000006ca9a1fc0> (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 アプリケーションのトラブルシューティング、監視、およびプロファイリングに役立つ GUI ツールです。 JVM も付属しており、Java インストールの /bin ディレクトリから起動できます。 非常に直感的で使いやすいです。 他のオプションの中でも、特定のプロセスのスレッド ダンプをキャプチャすることもできます。
特定のプロセスのスレッド ダンプを表示するには、プログラムを右クリックして、コンテキスト メニューから[スレッド ダンプ]を選択します。
#3。 jcmd
JCMD は、JDK に同梱されているコマンドライン ユーティリティであり、診断コマンド要求を JVM に送信するために使用されます。
ただし、Java アプリケーションが実行されているローカル マシンでのみ機能します。 Java Flight Recordings の制御、JVM および Java アプリケーションの診断とトラブルシューティングに使用できます。 jcmd の Thread.print コマンドを使用して、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 <0x000000076f380a88> (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 <0x000000076f388188> (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 <0x000000076f390180> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(Unknown Source) - locked <0x000000076f390180> (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 <0x000000076f398178> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Unknown Source) at java.lang.ref.Reference.tryHandlePending(Unknown Source) - locked <0x000000076f398178> (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.<init>(Unknown Source) at java.util.zip.ZipFile.<init>(Unknown Source) at java.util.zip.ZipFile.<init>(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 <0x000000076f85e348> (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
JMC は Java ミッション コントロールの略です。 これは、JDK に同梱されているオープンソースの GUI ツールであり、Java アプリケーション データの収集と分析に使用されます。
Java インストールの /bin フォルダーから起動できます。 Java の管理者と開発者は、このツールを使用して、JVM とアプリケーションの動作に関する詳細な低レベル情報を収集します。 Java Flight Recorder によって収集されたデータを詳細かつ効率的に分析できます。
jmc を起動すると、ローカル マシンで実行されている Java プロセスのリストが表示されます。 リモート接続も可能です。 特定のプロセスで、右クリックして[Start Flight Recording]を選択し、[Threads]タブでスレッド ダンプを確認します。
#5。 jconsole
jconsole は、苦情の管理と監視に使用される Java 管理拡張ツールです。
また、ユーザーが実行できる JMX エージェントでの定義済み操作のセットもあります。 これにより、ユーザーはライブ プログラムのスタック トレースを検出および分析できます。 Java インストールの /bin フォルダーから起動できます。
jconsole GUI ツールを使用して、実行中の Java プロセスにスレッドを接続するときに、各スレッドのスタック トレースを調べることができます。 次に、[スレッド]タブに、実行中のすべてのスレッドの名前が表示されます。 デッドロックを検出するには、ウィンドウの右下にある Detect Deadlock をクリックします。 デッドロックが検出された場合は、新しいタブに表示されます。それ以外の場合は、デッドロックが検出されませんでしたと表示されます。
#6。 ThreadMxBean
ThreadMXBean は、java.lang.Management パッケージに属する Java 仮想マシンのスレッド システムを管理するためのインターフェースです。 主に、デッドロック状態に陥ったスレッドを検出し、それらの詳細を取得するために使用されます。
ThreadMxBean インターフェイスを使用して、スレッド ダンプをプログラムでキャプチャできます。 ManagementFactory の getThreadMXBean() メソッドを使用して、ThreadMXBean インターフェイスのインスタンスを取得します。 デーモンと非デーモンの両方のライブ スレッドの数を返します。 ManagementFactory は、Java プラットフォームのマネージド Bean を取得するためのファクトリ クラスです。
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 (); }
スレッドダンプの手動分析
スレッド ダンプの分析は、マルチスレッド プロセスの問題を特定するのに非常に役立ちます。 個々のスレッド ダンプの状態を視覚化することで、デッドロック、ロック競合、個々のスレッド ダンプによる過剰な CPU 使用率などの問題を解決できます。
アプリケーションからの最大スループットは、スレッド ダンプを分析した後に各スレッドのステータスを修正することによって達成できます。
たとえば、プロセスが大量の CPU を使用しているとします。CPU を最も多く使用しているスレッドがあるかどうかを確認できます。 そのようなスレッドがあれば、その LWP 番号を 16 進数に変換します。 次に、スレッド ダンプから、前に取得した 16 進数と等しい nid を持つスレッドを見つけることができます。 スレッドのスタック トレースを使用して、問題を特定できます。 以下のコマンドを使用して、スレッドのプロセス ID を調べてみましょう。
ps -mo pid,lwp,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 <strong>Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):</strong> "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 . . . . . . . "<strong>Reference Handler</strong>" #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 <0x00000006c790fbd0> (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
では、スレッド ダンプを使用して調査できることを見てみましょう。 スレッド ダンプを観察すると、膨大な量のコンテンツが表示されます。 ただし、一度に 1 つずつ実行すると、非常に簡単に理解できます。 1行目を理解しよう
2020-06-27 09:01:29
フル スレッド ダンプ Java HotSpot(TM) 64 ビット サーバー VM (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: <strong>BLOCKED</strong> (on object monitor) at beans.ConnectionPool.getConnection(ConnectionPool.java:102) - waiting to lock <0xe0375410> (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: <strong>BLOCKED</strong> (on object monitor) at beans.ConnectionPool.getConnection(ConnectionPool.java:102) - waiting to lock <0xe0375410> (a beans.ConnectionPool) at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111) at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43) . . . .
デッドロックされたスレッドの分析
スレッド ダンプの非常に一般的に使用されるもう 1 つのアプリケーションは、デッドロックの検出です。 スレッド ダンプを分析すると、デッドロックの検出と解決がはるかに簡単になります。
デッドロックとは、1 つのスレッドが実行を継続するために必要なリソースが別のスレッドによってロックされていると同時に、2 番目のスレッドが必要とするリソースが最初のスレッドによってロックされている、少なくとも 2 つのスレッドが関係する状況です。
そのため、どのスレッドも実行を継続できず、デッドロック状態になり、アプリケーションがスタックしてしまいます。 ドレッドロックが存在する場合、スレッド ダンプの最後のセクションにデッドロックに関する情報が次のように出力されます。
"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 <0x00000000894465b0> (a java.lang.Object) - locked <0x00000000894465a0> (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 <0x00000000894465a0> (a java.lang.Object) - locked <0x00000000894465b0> (a java.lang.Object) at java.lang.Thread.run([email protected]/Thread.java:844)
ここでは、かなり人間が読める形式でデッドロック情報を見ることができます。
これ以外に、上記のスレッド ダンプのチャンクをすべて合計すると、以下の情報が示されます。
- 参照ハンドラは、人が読めるスレッドの名前です。
- #2 はスレッドの一意の ID です。
- daemon は、スレッドがデーモン スレッドかどうかを示します。
- スレッドの数値優先度は、prio=10 で指定されます。
- スレッドの現在のステータスは、待機状態で示されます。
- 次に、ロック情報を含むスタック トレースが表示されます。
スレッド ダンプ アナライザー
手動分析以外にも、オンラインとオフラインの両方でスレッド ダンプを分析するために使用できるツールが多数あります。 以下は、要件に基づいて使用できるツールの一部です。
まず、オンライン ツールについて調べてみましょう。
#1。 高速スレッド
高速スレッド 複雑な本番環境の問題をトラブルシューティングするための DevOps エンジニアのお気に入りのスレッド ダンプ分析ツールです。 これはオンライン Java スレッド ダンプ アナライザーです。スレッド ダンプをファイルとしてアップロードするか、スレッド ダンプを直接コピーして貼り付けることができます。
サイズに応じて、スレッド ダンプを分析し、スクリーンショットに示すように情報を表示します。
特徴
- JVM のクラッシュ、スローダウン、メモリ リーク、フリーズ、CPU スパイクのトラブルシューティング
- インスタント RCA (ベンダーを待つ必要はありません)
- 直感的なダッシュボード
- REST API のサポート
- 機械学習
#2。 Spotify スレッド ダンプ アナライザー
の Spotify スレッド ダンプ アナライザー Apache ライセンスのバージョン 2.0 の下でライセンスされています。 これはオンライン ツールであり、スレッド ダンプをファイルとして受け入れるか、スレッド ダンプを直接コピーして貼り付けることができます。 サイズに応じて、スレッド ダンプを分析し、スクリーンショットに示すように情報を表示します。
#3。 Jstackレビュー
Jstack.review ブラウザ内から Java スレッド ダンプを分析します。 このページはクライアント側のみです。
#4。 サイト24×7
これ 道具 Java 仮想マシン (JVM) のパフォーマンスを低下させる障害のあるスレッドを検出するための前提条件です。 個々のスレッド ダンプの状態を視覚化することで、デッドロック、ロック競合、個々のスレッド ダンプによる過剰な CPU 使用率などの問題を解決できます。
アプリからの最大スループットは、ツールによって提供される各スレッドのステータスを修正することによって達成できます。
それでは、オフライン ツールについて見ていきましょう。
プロファイリングに関しては、最高のツールだけで十分です。
#1。 JProfiler
JProfiler は、Java 開発者の間で最も人気のあるスレッド ダンプ アナライザーの 1 つです。 JProfiler の直感的な UI は、パフォーマンスのボトルネックを解決し、メモリ リークを特定し、スレッドの問題を理解するのに役立ちます。
JProfiler は、次のプラットフォームでのプロファイリングをサポートしています。
- ウィンドウズ
- マックOS
- Linux
- FreeBSD
- ソラリス
- AIX
- HP-UX
以下は、JVM でアプリケーションをプロファイリングするための JProfiler を最優先にするいくつかの機能です。
特徴
- JDBC、JPA、および NoSQL のデータベース プロファイリングをサポート
- Javaエンタープライズ版もサポート
- RMI 呼び出しに関する概要情報を提示します。
- メモリリークの優れた分析
- 広範な QA 機能
- 統合スレッド プロファイラーは、CPU プロファイリング ビューと緊密に統合されています。
- プラットフォーム、IDE、およびアプリケーション サーバーのサポート。
#2。 IBMTMDA
IBM Thread and Monitor Dump Analyzer for Java (TMDA) は、Java スレッド ダンプ内のハング、デッドロック、リソースの競合、およびボトルネックを特定できるツールです。 これは IBM 製品ですが、TMDA ツールは保証またはサポートなしで提供されます。 ただし、時間の経過とともにツールを修正および強化しようとします。
#3。 エンジンの管理
エンジンの管理 アプリケーション マネージャーは、JVM ヒープおよび非ヒープ メモリの監視に役立ちます。 しきい値を構成して、電子メールや SMS などでアラートを受け取ることもでき、Java アプリケーションが適切に調整されていることを確認できます。
#4。 あなたのキット
あなたのキット キットと呼ばれる以下の製品で構成されています。
- Java プロファイラー – Java EE および Java SE プラットフォーム用の完全な機能を備えた低オーバーヘッド プロファイラー。
- YouMonitor – Jenkins、TeamCity、Gradle、Maven、Ant、JUnit、TestNG のパフォーマンス監視とプロファイリング。
- .NET プロファイラー – .NET フレームワーク用の使いやすいパフォーマンスおよびメモリ プロファイラー。
結論
これで、スレッド ダンプがマルチスレッド アプリケーションの問題の理解と診断にどのように役立つかがわかりました。 適当に 知識、スレッド ダンプ (構造、含まれる情報など) に関しては、問題の原因を迅速に特定するために利用できます。