JRockit JDK ユーザーズ ガイド
![]() |
![]() |
![]() |
![]() |
スレッド ダンプ (または「スレッド スタック トレース」) は、アプリケーションのアクティビティに関する情報を明らかにします。この情報は、問題を診断したり、アプリケーションと JVM のパフォーマンスを最適化したりするのに役立ちます。たとえば、スレッド ダンプには、アプリケーションのパフォーマンスに深刻な影響を与える「デッドロック」状態の発生が示されます。
Control-Break
を呼び出して (通常は〔Ctrl
〕+〔Break
〕または〔Ctrl
〕+〔¥
〕を押すか、Linux 上で SIGQUIT
を入力する)、スレッド ダンプを作成することができます。この節では、スレッド ダンプの扱い方について説明します。内容は以下のとおりです。
Control-Break
または SIGQUIT
を使用してスタック トレースを出力すると、BEA JRockit はアクティブなロックのステータス (モニタ) も表示します。スレッドが待機状態にある場合、BEA JRockit はスレッドごとに以下のような情報を出力します。
Object.wait()
を呼び出して) スレッドがロック上で通知を待機している場合は、スタック トレースの先頭に「Waiting for notification」と表示されます。^-- Holding lock
」のように記述されます (^--
は、ロックがその上の行の関数で取得されたものであることを示しています)。注意 : コンパイラの最適化が原因で、ロック情報のある行が必ずしも正確ではない場合があります。これは、2 つのことを意味します。
foo()
内でメソッド bar()
を呼び出し、bar()
でロック A を取得した場合、そのロックは foo()
で取得されたものとして出力されることがあります。以上の点は通常は問題になりません。ロックの行の順序は正しい場所からそれほど大きく離れることはありません。また、ロックの行が失われることはなく、スレッドが取得したすべての行はスタック ダンプに必ず表示されます。
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
は BEA JRockit の内部的なロックのタイプを表します。現在は 3 種類のロックがあります。 コード リスト 3-1 に、1 つのスレッドのスタック トレースの例を示します。
コード リスト 3-1 例 : 1 つのスレッドのスタック トレース
"Open T1" prio=5 id=0x680 tid=0x128 waiting
-- Waiting for notification on: java/lang/Object@0x1060FFC8[fat lock]
at jrockit/vm/Threads.waitForSignalWithTimeout(Native Method)@0x411E39C0
at jrockit/vm/Locks.wait(Locks.java:1563)@0x411E3BE5
at java/lang/Thread.sleep(Thread.java:244)@0x41211045
^-- Lock released while waiting: java/lang/Object@0x1060FFC8[fat lock]
at test/Deadlock.loopForever(Deadlock.java:67)@0x412304FC
at test/Deadlock$LockerThread.run(Deadlock.java:57)@0x4123042E
^-- Holding lock: java/lang/Object@0x105BDCC0[recursive]
^-- Holding lock: java/lang/Object@0x105BDCC0[thin lock]
at java/lang/Thread.startThreadFromVM(Thread.java:1690)@0x411E5F73
--- End of stack trace
通常のスタック ダンプの後、BEA JRockit はデッドロックの検出を行います。まず、Java アプリケーションで「ロック チェーン」を検索します。ロック チェーンの循環が見つかると、アプリケーションはデッドロックに入っていると見なされます。
多少複雑に見えるかもしれませんが、ロック チェーンはかなり簡単なものです。ロック チェーンとは、一連のスレッドのそれぞれが、チェーン内の次のスレッドにより保持されているロックを待機している状態、と言えるでしょう。開いたロック チェーンとは、最後のスレッドがロックを取得しようとはせず、代わりに実際の作業をしているか何らかの外部イベントを待機している状態です。循環チェーンとはデッドロックであり、それが解決されることは決してありません。閉じたチェーンは、別のロック チェーンに依存しています。依存している別のロック チェーンがデッドロックであればそれもデッドロックとなり、別のロック チェーンが開いていればそれも開きます。閉じたチェーンとは、複数のスレッドが同じロックを取得しようとしている状態と言えるでしょう。
Ta->...->Tn
がロック チェーンであり、スレッド Ta
が保持しているロック La
が存在せず、スレッド Tx
がロック La
を取得しようとしている場合、そのロック チェーンは Ta
が始点となります。Ta->...->Tn
がスレッド Ta
を起点とするロック チェーンであり、存在しないロック Lx
をスレッド Tn
が取得しようとしている場合、Ta->...->Tn
は開いたロック チェーンであり、Tn
が終点となります。Ta->...->Tn
がスレッド Ta
を始点とするロック チェーンであり、スレッド To
が保持しているロック Lo
をスレッド Tn
が取得しようとしていて、しかもスレッド To
が別の完全なロック チェーンに含まれている場合、Ta->...->Tn
は閉じたロック チェーンであり、Tn
が終点 となります。上記の定義から、ロックを取得しようとしているすべてのスレッドが 1 つの完全なロック チェーンに含まれていることが理解できます。
BEA JRockit はすべての完全なロック チェーンを発見し、それらを開いたロック チェーン、閉じたロック チェーン、循環ロック チェーンにグループ分けします。開いたロック チェーンと閉じたロック チェーンはどれも、始点から終点まですべての要素が出力されます。循環ロック チェーンについては、始点も終点も存在しないため、任意の要素を選択してそれを始点として扱います。
閉じたチェーンと別のチェーンの間の区分は任意です。2 つの異なるスレッドが同じロックを取得しようとしてブロックされると、閉じたチェーンが発生します。たとえば、Thread A
が保持している Lock A
を、Thread B
と Thread C
の両者が待機している場合などです。BEA JRockit はこの状況を次のいずれかに解釈します。
デッドロックしたロック チェーンは解決できないため、アプリケーションは無限に待機してスタックする可能性があります。長くて開いたロック チェーンがある場合、アプリケーションはロックを待機して時間を無駄に消費する可能性があります。
![]() |
![]() |
![]() |