Interstage Application Server/Interstage Web Server チューニングガイド
目次 索引 前ページ次ページ

第7章 JDK/JREのチューニング> 7.3 チューニング/デバッグ技法

7.3.3 スレッドダンプ

 スレッドダンプには、Javaプロセスの各スレッドの情報(スタックトレース形式)が含まれているため、ハングアップやデッドロックなどの動作具合を調査することができます。

 スレッドダンプの出力先は、標準出力です。スレッドダンプが出力される契機および出力先を、表1に示します。

表1 スレッドダンプの出力契機と出力先

プログラムの種類

出力契機

出力先

J2EEアプリケーション

一定の条件を満たした場合、コンテナの機能により自動的に採取される場合と利用者の任意のタイミングで手動による採取があります。

自動採取:

アプリケーションがタイムアウトまたは無応答になった場合

手動採取:


 “スレッドダンプツール”で採取することができます。スレッドダンプツールの詳細は、“トラブルシューティング集”の“スレッドダンプツール”を参照してください。


 kill -QUIT [プロセスID]でJava VMに対してQUITシグナルを送り採取することができます。

コンテナ情報ログ(info.log)

J2EE以外のJavaプログラム

利用者の任意のタイミングで手動で採取することができます。


コマンドプロンプトからJavaプログラムを起動した場合:

以下、いずれかの方法で採取できます。

1)[Ctrl]+[Break]キー押下

2)“スレッドダンプツール”

コマンドプロンプト以外からJavaプログラムを起動した場合:

“スレッドダンプツール”で採取します。

スレッドダンプツールの詳細は、“トラブルシューティング集”の“スレッドダンプツール”を参照してください。


ターミナルからJavaプログラムを起動した場合:

以下、いずれかの方法で採取できます。

1)[Ctrl]+[\]キー(英語キーボードの場合バックスラッシュキー)押下

2)kill -QUIT [プロセスID]

ターミナル以外からJavaプログラムを起動した場合:

kill -QUIT [プロセスID]で採取します。


 “-Xrs”オプションが指定されたJavaプロセスの場合、当該プロセスへ送られた[Ctrl]+[Break]キー押下またはQUITシグナルに対する動作は、OSのデフォルト動作になります。
 そのため、“-Xrs”オプションを指定したJavaプロセスに対して[Ctrl]+[Break]キー押下またはQUITシグナルが送られると、当該Javaプロセスは強制終了または異常終了します。
 スレッドダンプを出力する可能性があるJavaプロセスに対して、“-Xrs”オプションは指定しないでください。
 ただし、Windows(R)でサービスとして登録されるJavaプロセスの場合は、“-Xrs”オプションを指定しない場合、ログオフ時に強制終了してしまいます。これが不都合な場合は、“-Xrs”オプションを指定してください。

コンソール(標準出力)

 図1の出力例をもとにして、スレッドダンプの解析方法を説明します。

図1 サンプルプログラム

1 :public class DeadlockSample {
2 : static boolean flag;
3 : static Thread1 thread1;
4 : static Thread2 thread2;
5 :
6 : public static void main(String[] args) {
7 : thread1 = new Thread1();
8 : thread2 = new Thread2();
9 : thread1.start();
10: thread2.start();
11: }
12:}
13:
14:class Thread1 extends Thread {
15: public Thread1(){
16: super("Thread1");
17: }
18:
19: public void run(){
20: synchronized(this){
21: System.out.println("Thread1開始");
22: while(DeadlockSample.flag==false){ // Thread2が開始するのを待つ
23: yield();
24: }
25: DeadlockSample.thread2.method();
26: notify();
27: }
28: }
29:
30: public synchronized void method(){
31: try{wait(1000);}catch(InterruptedException ex){}
32: System.out.println("Thread1.method()終了");
33: }
34:}
35:
36:class Thread2 extends Thread {
37: public Thread2(){
38: super("Thread2");
39: }
40:
41: public void run() {
42: synchronized(this){
43: DeadlockSample.flag = true;
44: System.out.println("Thread2開始");
45: DeadlockSample.thread1.method();
46: notify();
47: }
48: }
49:
50: public synchronized void method() {
51: try{wait(1000);}catch(InterruptedException ex){}
52: System.out.println("Thread2.method()終了");
53: }
54:}

 図1のサンプルでは、Thread1とThread2がお互いに排他処理を行っています。
 このサンプルを実行すると、次のように処理が進められます。

  1. Thread1で、Thread1のロックを獲得する(20行目のsynchronized節)
  2. Thread2で、Thread2のロックを獲得する(42行目のsynchronized節)
  3. Thread1で、Thread2.method()を実行しようとして、ロック解放待ちになる(50行目のsynchronized修飾子)
  4. Thread2で、Thread1.method()を実行しようとして、ロック解放待ちになる(50行目のsynchronized修飾子)

 この結果、Thead1とThread2がお互いに、解放されないロックを待ち続けるデッドロック状態になります。
 デッドロック状態で、スレッドダンプを採取したものを、図2に示します。

図2 スレッドダンプ

"DestroyJavaVM" prio=5 tid=0x002856c8 nid=0x5f4 waiting on condition [0..6fad8]

"Thread2" prio=5 tid=0x0092f4d8 nid=0x640 waiting for monitor entry [182ef000..182efd64]
at Thread1.method(DeadlockSample.java:31)
- waiting to lock <0x1002ffe8> (a Thread1)
at Thread2.run(DeadlockSample.java:45)
- locked <0x10030ca0> (a Thread2)

"Thread1" prio=5 tid=0x0092f370 nid=0x294 waiting for monitor entry [182af000..182afd64]
at Thread2.method(DeadlockSample.java:51)
- waiting to lock <0x10030ca0> (a Thread2)
at Thread1.run(DeadlockSample.java:25)
- locked <0x1002ffe8> (a Thread1)

"Signal Dispatcher" daemon prio=10 tid=0x0098eb80 nid=0x634 waiting on condition [0..0]

"Finalizer" daemon prio=9 tid=0x0092a540 nid=0x5e8 in Object.wait() [1816f000..1816fd64]
at java.lang.Object.wait(Native Method)
- waiting on <0x10010498> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:111)
- locked <0x10010498> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:127)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)

"Reference Handler" daemon prio=10 tid=0x0096da70 nid=0x5e4 in Object.wait() [1812f000..1812fd64]
at java.lang.Object.wait(Native Method)
- waiting on <0x10010388> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:429)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:115)
- locked <0x10010388> (a java.lang.ref.Reference$Lock)

"VM Thread" prio=5 tid=0x0096c950 nid=0x624 runnable

"VM Periodic Task Thread" prio=10 tid=0x0092c008 nid=0x2a0 waiting on condition
"Suspend Checker Thread" prio=10 tid=0x0098e118 nid=0x478 runnable

Found one Java-level deadlock:
=============================
"Thread2":
waiting to lock monitor 0x00929c3c (object 0x1002ffe8, a Thread1),
which is held by "Thread1"
"Thread1":
waiting to lock monitor 0x00929c5c (object 0x10030ca0, a Thread2),
which is held by "Thread2"

Java stack information for the threads listed above:
===================================================
"Thread2":
at Thread1.method(DeadlockSample.java:31)
- waiting to lock <0x1002ffe8> (a Thread1)
at Thread2.run(DeadlockSample.java:45)
- locked <0x10030ca0> (a Thread2)
"Thread1":
at Thread2.method(DeadlockSample.java:51)
- waiting to lock <0x10030ca0> (a Thread2)
at Thread1.run(DeadlockSample.java:25)
- locked <0x1002ffe8> (a Thread1)

Found 1 deadlock.

■解析方法

 スレッドダンプの各スレッドの情報は、スタックトレース形式です。
 Thread1とThread2の両方のスタックトレースには、“locked”と“waiting to lock”があります。また、スレッドダンプの下の方にも、“deadlock”の文字列があり、デットロックが発生していることが確認できます。

 このように、スレッドダンプで全スレッドの動作状況を確認するにより、Javaプロセスがハングアップしているか、あるいは、デッドロック状態かを確認することができます。特に、短い間隔で複数のスレッドダンプを採取し、スレッドに動きがなければ、ハングアップの可能性があります。

 スレッドダンプの詳細は、“トラブルシューティング集”の“スレッドダンプが出力された場合の対処”も参照してください。


目次 索引 前ページ次ページ

Copyright 2008 FUJITSU LIMITED