ヘッダーをスキップ
Oracle® JRockit JDKツール・ガイド
リリースR28
B61440-03
  目次へ移動
目次

前
 
次
 

3 スレッド・ダンプの使用

この章では、Oracle JRockit JVMのスレッド・ダンプの取得方法と使用方法について説明します。スレッドおよびスレッドの同期化に関する基本情報については、『Oracle JRockit JDKの紹介』を参照してください。

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

スレッド・ダンプは、アプリケーションのスレッド・アクティビティに関する情報を示します。問題を診断し、アプリケーションおよびJVMのパフォーマンスを最適化するのに役立ちます。たとえば、スレッド・ダンプにはデッドロックの発生が自動的に表示されます。デッドロックが発生すると、アプリケーションの一部または全部が完全な停止状態になります。

この章では、次の節について説明します。

3.1 スレッド・ダンプの作成

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

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


注意:

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


3.2 スレッド・ダンプの内容

この項ではスレッド・ダンプの一般的な内容について説明します。スレッド・ダンプの例が最初から最後まで表示されます。例3-1例3-2例3-3例3-4および例3-5はスレッド・ダンプの各コンポーネントを表しています。次にメイン・スレッドに関する情報が表示されてから、JVMのすべての内部スレッド、その後に他のすべてのJavaアプリケーション・スレッド(ある場合)が続きます。最後にロック・チェーンに関する情報が表示されます。

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

3.2.1 スレッド・ダンプの先頭

スレッド・ダンプは、ダンプの日時と、使用されているJRockit JVMのバージョン番号で始まります。例3-1を参照してください。

例3-1 スレッド・ダンプの先頭の情報

===== FULL THREAD DUMP ===============
Wed Feb 21 13:46:45 2007
Oracle JRockit(R) R28.0.0-109-73164-1.5.0_08-20061129-1428-windows-ia32

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

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

例3-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

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

3.2.3 ロックとロック・チェーン

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

  • スレッドがロックを取得しようとした(同期ロックに入ろうとした)ものの、別のスレッドがすでにロックを保持している場合は、スタック・トレースの先頭に表示されます(Blocked trying to get lock)。

  • (Object.wait()を呼び出して)スレッドがロック上で通知を待機している場合は、スタック・トレースの先頭に表示されます(Waiting for notification)。

  • スレッドがロックを取得した場合、これがスタック・トレースに表示されます。関数呼出しを表すスタック・トレース内の行の後に、その関数でスレッドが取得したロックのリストがあります。これは^-- Holding lockのように記述されます(^--は、ロックがその上の行の関数で取得されたものであることを示しています)。

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は異なる可能性があります。LockTypeはJVMの内部的なタイプのロック(ファット、シン、再帰的、または遅延)を表します。アクティブなロック(モニター)のステータスもスタック・トレースに表示されます。

3.2.3.1 ロックの表示順序の相違

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

  • あるスレッドが同じ関数内で最初にロックAを取得し、次にロックBを取得した場合、これらのロックが出力される順序は不特定です。

  • あるスレッドがメソッドedit()内でメソッドsave()を呼び出し、save()でロックAを取得した場合、そのロックはedit()で取得されたものとして出力されることがあります。

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

3.2.4 JVM内部スレッド

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

例3-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

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

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

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

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

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

例3-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-1がブロックされている間、Thread-0とThread-2がデッドロックを発生させています。

3.2.6 ロック・チェーン

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

例3-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.3 スレッド・ダンプ内のスレッドのステータス

この項では、スレッド・ダンプ内のスレッドの様々なステータスまたは状態を説明します。

3.3.1 活動状態

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

表3-1 スレッドの活動状態

状態 説明

alive

これは通常の実行中のスレッドです。実質的に、スレッド・ダンプ内のすべてのスレッドは(alive)になります。

not started

java.lang.Thread.start()によってスレッドの実行開始がリクエストされていますが、実際のOSプロセスがまだ開始していないか、またはJRockit JVMに制御を渡すところまで実行されていません。この値が表示されることはめったにありません。作成済みですがstart()が実行されていないjava.lang.Threadオブジェクトは、スレッド・ダンプに表示されません。

terminated

このスレッドはrun()を終了し、関係するすべてのスレッドに通知も送信しましたが、実行中のスレッド用の内部JVMスレッド構造にまだ保持されています。この値が表示されることはめったにありません。終了してから数ミリ秒が経過したスレッドは、スレッド・ダンプに表示されません。


3.3.2 実行状態

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

表3-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エージェントの呼出しによって一時停止されました。


3.3.3 特別状態

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

表3-3 スレッドの特別状態

状態 説明

interrupted

ユーザーはこのスレッドでjava.lang.Thread.interrupt()を呼び出しました。

daemon

これは、JVM内部スレッドか、java.lang.Thread.setDaemon()でデーモン・スレッドとしてマークされたスレッドのどちらかです。

in native

このスレッドは、ユーザーが指定したJNIコードまたはJVM内部コードのいずれかのネイティブ・コードを実行しています。

in suspend critical mode

このスレッドはJVM内部コードを実行中で、自身をsuspend criticalとしてマークしています。ガベージ・コレクションは指定された期間、停止します。

native_blocked

このスレッドはJVM内部コードを実行中で、JVM内部ロックを取得しようとしています。該当するロックは別のスレッドで保持されているため、スレッドはブロックされています。

native_waiting

このスレッドはJVM内部コードを実行中で、JVM内部ロックに関する別のスレッドからの通知を待機しています。


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

この項では、トラブルシューティングと診断にスレッド・ダンプを使用するための情報を示します。

デッドロックの検出だけでなく、トラブルシューティングにもスレッド・ダンプを使用するには、同じプロセスから複数のスレッド・ダンプを取得する必要があります。動作の長時間の分析を行う場合は、複数のスレッド・ダンプと、他の診断ツール(Oracle JRockit Mission Controlの一部であるJRockitフライト・レコーダなど)を組み合せる方が効果的です。

3.4.1 デッドロックの検出

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

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

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

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

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

複数の連続したスレッド・ダンプを作成すると、Javaアプリケーションでどの部分が最も頻繁に使用されているのかを調べることができます。アプリケーションの様々な部分の負荷に関する詳細情報については、JRockit管理コンソールの「スレッド」タブをクリックしてください。