診断ガイド

     前  次    目次     
コンテンツの開始位置

スレッド ダンプの使用

この章では、Oracle JRockit JVM のスレッド ダンプの取得方法と使用方法について説明します。スレッドおよびスレッドの同期化に関する基礎的な情報については、「スレッドとロックの概要」を参照してください。

スレッド ダンプは、プロセスの一部となっているすべてのスレッドの状態のスナップショットです。各スレッドの状態は、スレッドのスタックの内容を示した「スタック トレース」に表示されます。スレッドには、実行中の Java アプリケーションに属するスレッドや、JVM 内部スレッドがあります。

スレッド ダンプではアプリケーションのスレッド アクティビティに関する情報が示されます。この情報は、問題を診断してアプリケーションや JVM のパフォーマンスを最適化するのに役立ちます。たとえば、スレッド ダンプにはデッドロックの発生が自動的に表示されます。デッドロックはアプリケーションの一部または全部を完全に中断させるものです。

この章の内容は以下のとおりです。

 


スレッド ダンプの作成

プロセスからスレッド ダンプを作成するには、次のいずれかを実行します。

スレッド ダンプがコマンドラインに表示されます。

注意 : jrcmd と Ctrl-Break ハンドラの詳細については、「診断コマンドの実行」を参照してください。

 


スレッド ダンプの内容

この節では、スレッド ダンプのサンプルに最初から最後まで目を通しながら、スレッド ダンプの一般的な内容について説明します。スレッド ダンプのサンプルはいくつかの部分に分かれています (コード リスト 20-1コード リスト 20-2コード リスト 20-3コード リスト 20-4 およびコード リスト 20-5 を参照)。最初にメイン スレッドに関する情報が出力され、次に JVM のすべての内部スレッド、その後に、他のすべての Java アプリケーション スレッド (ある場合) が続きます。最後にロック チェーンに関する情報が出力されます。

サンプルのスレッド ダンプは、3 つのスレッドを作成し、そのスレッドがすぐにデッドロックに入るというプログラムから取得したものです。アプリケーション スレッド Thread-0、Thread-1、および Thread-2 は、Java コード内の 3 つの異なるクラスに対応しています。

スレッド ダンプの先頭

スレッド ダンプは、ダンプの日時と、使用されている JRockit JVM のバージョン番号で始まります (コード リスト 20-1 を参照)。

コード リスト 20-1 スレッド ダンプの最初の情報
===== FULL THREAD DUMP ===============
Wed Feb 21 13:46:45 2007
BEA JRockit(R) R27.1.0-109-73164-1.5.0_08-20061129-1428-windows-ia32

メイン アプリケーション スレッドのスタック トレース

コード リスト 20-2 に、メイン アプリケーション スレッドのスタック トレースを示します。スレッド情報の行があり、その後に、ロックに関する情報とスレッド ダンプの時点でのスレッドのスタック トレースが続きます。

コード リスト 20-2 スレッド ダンプのメイン スレッド
"Main Thread" id=1 idx=0x2 tid=48652 prio=5 alive, in native, waiting
-- Waiting for notification on: util/repro/Thread1@0x01226528[fat lock]
at jrockit/vm/Threads.waitForSignal(J)Z(Native Method)
at java/lang/Object.wait(J)V(Native Method)
at java/lang/Thread.join(Thread.java:1095)
^-- Lock released while waiting: util/repro/Thread1@0x01226528[fat lock]
at java/lang/Thread.join(Thread.java:1148)
at util/repro/DeadLockExample.main(DeadLockExample.java:23)
at jrockit/vm/RNI.c2java(IIII)V(Native Method)
-- end of trace

名前と他の識別情報の後に、メイン スレッドのさまざまなステータス メッセージが出力されます。コード リスト 20-2 のメイン スレッドは実行中のスレッドであり (alive)、JVM の内部コードまたはユーザ定義の JNI コードを実行していて (in native)、オブジェクトの解放を待機していることろです (waiting)。(Object.wait() を呼び出して) スレッドがロック上で通知を待機している場合は、スタック トレースの先頭に「Waiting for notification on」と表示されます。

ロックとロック チェーン

各スレッドについて、JRockit JVM は以下の情報を出力します。

Java のオブジェクトにおける (通知の) 待機のセマンティクスは多少複雑です。同期ブロックに入るには、最初にオブジェクトのロックを取得し、次にそのオブジェクトの wait() を呼び出す必要があります。wait メソッドでは、スレッドが通知を待機して実際にスリープ状態に入る前に、ロックが解放されます。通知を受け取ると、wait はロックを返す前に再び取得します。したがって、スレッドがロックを取得し、そのロック上で通知を待機している場合、ロックの取得時に記述されるスタック トレースの行には、「Holding lock」ではなく「Lock released while waiting」と表示されます。

すべてのロックは Classname@0xLockID[LockType] の形式で記述されます。次に例を示します。

java/lang/Object@0x105BDCC0[thin lock]

Classname@0xLockID はロックが属しているオブジェクトを表します。Classname はオブジェクトの実際の完全修飾クラス名です。一方、LockID は 1 つのスレッド ダンプでのみ有効な一時的な ID です。つまり、1 つのスレッド ダンプで、スレッド A がロック java/lang/Object@0x105BDCC0 を保持していて、スレッド B がロック java/lang/Object@0x105BDCC0 を待機している場合、それらは同じロックであると考えることができます。ただし、それ以降に別のスレッド ダンプを出力した場合は、LockID を比較できません。スレッドが同じロックを保持したとしても、異なる LockID になる可能性があります。反対に、LockID が同じだとしても、同じロックを保持していることにはなりません。LockType は JVM の内部的なタイプのロック (ファット、シン、再帰的、または遅延) を表します。アクティブなロックのステータス (モニタ) もスタック トレースに表示されます。

ロックの表示順序の相違

コンパイラによる最適化が原因で、表示されるロック情報が必ずしも正確ではない場合があります。これは、2 つのことを意味します。

以上の点は通常は問題になりません。ロックの行の順序は正しい場所からそれほど大きく離れることはありません。また、ロックの行が失われることはなく、スレッドが取得したすべての行はスレッド ダンプに必ず表示されます。

JVM 内部スレッド

コード リスト 20-3 に、JVM 内部スレッドのトレースを示します。daemon という表示からわかるように、このスレッドはデーモン スレッドとしてマークされています。デーモン スレッドは、(この例のように) JVM 内部スレッドの場合と、java.lang.Thread.setDaemon() でデーモン スレッドとしてマークされたスレッドである場合があります。

コード リスト 20-3 JVM 内部スレッドのリスト内で最初と最後のスレッド
"(Signal Handler)" id=2 idx=0x4 tid=48668 prio=5 alive, in native, daemon
[...]
"(Sensor Event Thread)" id=10 idx=0x1c tid=48404 prio=5 alive, in native, daemon

コード リスト 20-3 では、JVM 内部スレッドに関するロック情報とスタック トレースは出力されていません。これはデフォルトの設定です。

JVM 内部スレッドのスタック トレースを表示する場合は、print_threads ハンドラを送信するときにパラメータ nativestack=true を使用します。コマンドラインで、次のように入力します。

bin\jrcmd.exe <pid> print_threads nativestack=true

他の Java アプリケーションのスレッド

通常、ユーザが主に関心を寄せるのは実行中の Java アプリケーションのスレッド (メイン スレッドを含む) です。メイン スレッド以外のすべての Java アプリケーション スレッドは、スレッド ダンプの最後の方に表示されます。コード リスト 20-4 は、3 つの異なるアプリケーション スレッドのスタック トレースを示しています。

コード リスト 20-4 追加のアプリケーション スレッド
"Thread-0" id=11 idx=0x1e tid=48408 prio=5 alive, in native, blocked
-- Blocked trying to get lock: java/lang/Object@0x01226300[fat lock]
at jrockit/vm/Threads.waitForSignal(J)Z(Native Method)
at jrockit/vm/Locks.fatLockBlockOrSpin(ILjrockit/vm/ObjectMonitor;II)V(Unknown Source)
at jrockit/vm/Locks.lockFat(Ljava/lang/Object;ILjrockit/vm/ObjectMonitor;Z)Ljava/lang/Object;(Unknown Source)
at
jrockit/vm/Locks.monitorEnterSecondStage(Ljava/lang/Object;I)Ljava/lang/Object;(Unknown Source)
at jrockit/vm/Locks.monitorEnter(Ljava/lang/Object;)Ljava/lang/Object;(Unknown Source)
at util/repro/Thread1.run(DeadLockExample.java:34)
^-- Holding lock: java/lang/Object@0x012262F0[thin lock]
^-- Holding lock: java/lang/Object@0x012262F8[thin lock]
at jrockit/vm/RNI.c2java(IIII)V(Native Method)
-- end of trace
"Thread-1" id=12 idx=0x20 tid=48412 prio=5 alive, in native, blocked
-- Blocked trying to get lock: java/lang/Object@0x012262F8[thin lock]
at jrockit/vm/Threads.sleep(I)V(Native Method)
at jrockit/vm/Locks.waitForThinRelease(Ljava/lang/Object;I)I(Unknown Source)
at jrockit/vm/Locks.monitorEnterSecondStage(Ljava/lang/Object;I)Ljava/lang/Object;(Unknown Source)
at jrockit/vm/Locks.monitorEnter(Ljava/lang/Object;)Ljava/lang/Object;(Unknown Source)
at util/repro/Thread2.run(DeadLockExample.java:48)
at jrockit/vm/RNI.c2java(IIII)V(Native Method)
-- end of trace

"Thread-2" id=13 idx=0x22 tid=48416 prio=5 alive, in native, blocked
-- Blocked trying to get lock: java/lang/Object@0x012262F8[thin lock]
at jrockit/vm/Threads.sleep(I)V(Native Method)
at jrockit/vm/Locks.waitForThinRelease(Ljava/lang/Object;I)I(Unknown Source)
at jrockit/vm/Locks.monitorEnterSecondStage(Ljava/lang/Object;I)Ljava/lang/Object;(Unknown Source)
at jrockit/vm/Locks.monitorEnter(Ljava/lang/Object;)Ljava/lang/Object;(Unknown Source)
at util/repro/Thread3.run(DeadLockExample.java:65)
^-- Holding lock: java/lang/Object@0x01226300[fat lock]
at jrockit/vm/RNI.c2java(IIII)V(Native Method)
-- end of trace

3 つのスレッドはすべてブロックされた状態です (blocked として表示)。つまり、スレッドは同期化ブロックに入ろうとしています。Thread-0 は Object@0x01226300[fat lock] を取得しようとしていますが、このロックは Thread-2 が保持しています。Thread-2 と Thread-1 の両方とも Object@0x012262F8[thin lock] を取得しようとしていますが、このロックは Thread-0 が保持しています。つまり、Thread-0 と Thread-2 はデッドロックを形成し、Thread-1 はブロックされています。

ロック チェーン

JRockit JVM の優れた機能の 1 つは、実行中のスレッドの中で、デッドロックしたロック チェーン、ブロックされたロック チェーン、開いたロック チェーンを自動的に検出することです。コード リスト 20-5 の分析結果には、スレッド T1、T2、T3、T4 および T5 によって形成されたすべてのロック チェーンが示されています。この情報を使用して、Java コードのチューニングやトラブルシューティングを行えます。

コード リスト 20-5 デッドロックしたチェーンとブロックされたロック チェーン
Circular (deadlocked) lock chains
=================================
Chain 6:
"Dead T1" id=16 idx=0x48 tid=3648 waiting for java/lang/Object@0x01225018 held by:
"Dead T3" id=18 idx=0x50 tid=900 waiting for java/lang/Object@0x01225010 held by:
"Dead T2" id=17 idx=0x4c tid=3272 waiting for java/lang/Object@0x01225008 held by:
"Dead T1" id=16 idx=0x48 tid=3648
Blocked lock chains
===================
Chain 7:
"Blocked T2" id=20 idx=0x58 tid=3612 waiting for java/lang/Object@0x01225310 held by:
"Blocked T1" id=19 idx=0x54 tid=2500 waiting for java/lang/Object@0x01224B60 held by:
"Open T3" id=13 idx=0x3c tid=1124 in chain 1
Open lock chains
================
Chain 1:
"Open T5" id=15 idx=0x44 tid=4048 waiting for java/lang/Object@0x01224B68 held by:
"Open T4" id=14 idx=0x40 tid=3380 waiting for java/lang/Object@0x01224B60 held by:
"Open T3" id=13 idx=0x3c tid=1124 waiting for java/lang/Object@0x01224B58 held by:
"Open T2" id=12 idx=0x38 tid=3564 waiting for java/lang/Object@0x01224B50 held by:
"Open T1" id=11 idx=0x34 tid=2876 (active)

 


スレッド ダンプ内のスレッドのステータス

この節では、スレッドがスレッド ダンプに示す可能性のあるさまざまなステータス (状態) について説明します。3 つのタイプの状態があります。

活動状態

スレッドがスレッド ダンプに示す可能性のある活動状態を表 20-1 に示します。

表 20-1 スレッドの活動状態
状態
説明
alive
これは通常の実行中のスレッドである。実質的に、スレッド ダンプ内のすべてのスレッドは alive 状態である。
not started
java.lang.Thread.start() によってスレッドの実行開始が要求されているが、実際の OS プロセスがまだ開始していない、または JRockit JVM に制御を渡すところまで実行されていない。この値が表示されることは、めったにない。作成されているが start() が実行されていない java.lang.Thread オブジェクトは、スレッド ダンプに表示されない。
terminated
このスレッドは run() メソッドを終了し、関係するすべてのスレッドに通知も送信したが、実行中のスレッド用の内部 JVM スレッド構造にまだ保持されている。この値が表示されることは、めったにない。終了してから数ミリ秒が経過したスレッドは、スレッド ダンプに表示されない。

実行状態

スレッドがスレッド ダンプに示す可能性のある実行状態を表 20-2 に示します。

表 20-2 スレッドの実行状態
状態
説明
blocked
このスレッドは同期ブロックに入ろうとしたが、別のスレッドがロックを取得している。ロックが解除されるまで、このスレッドはブロックされる。
blocked (on thin lock)
これは blocked と同じ状態だが、該当のロックがシン ロックであるという追加情報を伴う。
waiting
このスレッドは、オブジェクトの Object.wait() を呼び出した。別のスレッドが該当のオブジェクトに通知を送信するまで、スレッドはそのままの状態になる。
sleeping
このスレッドは java.lang.Thread.sleep() を呼び出した。
parked
このスレッドは java.util.concurrent.locks.LockSupport.park() を呼び出した。
suspended
スレッドの実行は java.lang.Thread.suspend() または JVMTI/JVMPI エージェントの呼び出しによってサスペンドされた。

特別状態

スレッドがスレッド ダンプに示す可能性のある特別状態を表 20-3 に示します。これらの状態はすべて相互に排他的ではありません。

 


スレッド ダンプによるトラブルシューティング

この節では、トラブルシューティングと診断にスレッド ダンプを使用する方法について説明します。

デッドロックの検出だけでなく、トラブルシューティングにもスレッド ダンプを使用するには、同じプロセスから複数のスレッド ダンプを取得する必要があります。ただし、動作の長時間の分析を行う場合は、複数のスレッド ダンプと、他の診断ツールを組み合わせる方が効果的です。たとえば、Oracle JRockit Mission Control の一部である JRockit Runtime Analyzer を使用します (詳細については、「Oracle JRockit Mission Control ツールの使用」を参照してください)。

デッドロックの検出

Oracle JRockit JVM は自動的にスレッド ダンプ情報を分析して、循環 (デッドロックした) ロック チェーンやブロックされたロック チェーンが存在するかどうかを検出します。

処理のボトルネックの検出

スレッド内のデッドロック以外のものを検出するには、複数のスレッド ダンプを連続して作成する必要があります。それにより、競合の発生 (複数のスレッドが同じロックを取得しようとすること) を検出できます。競合の場合、デッドロックしてはいないが、長い開いたロック チェーンが形成される可能性があり、パフォーマンスの低下につながります。

(連続したスレッド ダンプで)、アプリケーションの 1 つまたは複数のスレッドがロックの解放を待機して一時的にスタックしていることを見つけた場合は、Java アプリケーションのコードを調べて、同期化 (シリアライゼーション) が必要かどうか、スレッドを別の方法で編成できるかどうかを確認してください。

アプリケーションの実行時プロファイルの表示

複数の連続したスレッド ダンプを作成すると、Java アプリケーションでどの部分が最も頻繁に使用されているのかを素早く調べることができます。ただし、アプリケーションのさまざまな部分の負荷に関する詳細情報については、JRockit Management Console の [スレッド] タブを確認してください。


  ページの先頭       前  次