2.1.1 ヒープ割り当てプロファイル (heap=sites)
2.1.3 CPU 使用率サンプリングプロファイル (cpu=samples)
2.1.4 CPU 使用時間プロファイル (cpu=times)
2.4.3 別のマシン上のコアファイルまたはハングアッププロセスへの接続
2.5.3.2 このオブジェクトはどこで割り当てられたのか。
2.16.4 Solaris 10 OS で導入されたツール
2.17.1 java.lang.management パッケージ
2.17.2 java.lang.instrument パッケージ
この章では、JDK 7 で使用可能なトラブルシューティングツールについて詳しく説明します。さらにこの章では、これらのトラブルシューティングツールと組み合わせて使用可能なオペレーティングシステム固有のツールの一覧を示します。最後にこの章では、JDK 7 で提供される API を使って新しいツールをどのようにして開発できるかを説明します。
この章には、次のセクションがあります。
ヒーププロファイラ (HPROF) ツールは、JDK リリースに付属する単純なプロファイラエージェントです。これは、Java Virtual Machine Tools Interface (JVM TI) を使って Java VM とインタフェースする、動的にリンクされたライブラリです。このツールは、プロファイル情報をファイルまたはソケットに ASCII 形式またはバイナリ形式で書き込みます。この情報はさらに、プロファイラフロントエンドツールを使って処理できます。
HPROF ツールでは、CPU 使用率、ヒープ割り当ての統計、およびモニター競合プロファイルを表示できます。さらに、完全なヒープダンプや、Java 仮想マシン内のすべてのモニターとスレッドの状態を報告できます。問題の診断に関して、HPROF はパフォーマンス、ロック競合、メモリーリークなどの問題を分析する際に役立ちます。
JDK リリースには HPROF ライブラリだけでなく、HPROF のソースも JVM TI のデモコードとして含まれています。このコードは $JAVA_HOME/demo/jvmti/hprof ディレクトリにあります。
HPROF ツールは次のように呼び出されます。
$ java -agentlib:hprof ToBeProfiledClass
HPROF は要求されたプロファイルの種類に応じて、関連するイベントを送信するように仮想マシンに指示します。その後、ツールはイベントデータをプロファイル情報に処理します。たとえば、次のコマンドはヒープ割り当てプロファイルを取得します。
$ java -agentlib:hprof=heap=sites ToBeProfiledClass
次に示すように、HPROF エージェントに help オプションを指定すると、オプションの完全な一覧が出力されます。
$ java -agentlib:hprof=help HPROF: Heap and CPU Profiling Agent (JVMTI Demonstration Code) hprof usage: java -agentlib:hprof=[help]|[<option>=<value>, ...] Option Name and Value Description Default --------------------- ----------- ------- heap=dump|sites|all heap profiling all cpu=samples|times|old CPU usage off monitor=y|n monitor contention n format=a|b text(txt) or binary output a file=<file> write data to file java.hprof[{.txt}] net=<host>:<port> send data over a socket off depth=<size> stack trace depth 4 interval=<ms> sample interval in ms 10 cutoff=<value> output cutoff point 0.0001 lineno=y|n line number in traces? y thread=y|n thread in traces? n doe=y|n dump on exit? y msa=y|n Solaris micro state accounting n force=y|n force output to <file> y verbose=y|n print messages about dumps y Obsolete Options ---------------- gc_okay=y|n <> Examples -------- - Get sample cpu information every 20 millisec, with a stack depth of 3: java -agentlib:hprof=cpu=samples,interval=20,depth=3 classname - Get heap usage information based on the allocation sites: java -agentlib:hprof=heap=sites classname Notes ----- - The option format=b cannot be used with monitor=y. - The option format=b cannot be used with cpu=old|times. - Use of the -Xrunhprof interface can still be used, e.g. java -Xrunhprof:[help]|[<option>=<value>, ...] will behave exactly the same as: java -agentlib:hprof=[help]|[<option>=<value>, ...] Warnings -------- - This is demonstration code for the JVMTI interface and use of BCI, it is not an official product or formal part of the JDK. - The -Xrunhprof interface will be removed in a future release. - The option format=b is considered experimental, this format may change in a future release.
ヒーププロファイル情報 (サイトとダンプ) はデフォルトで、現在の作業ディレクトリ内の java.hprof.txt に (ASCII で) 書き出されます。
出力は通常、VM 終了時に生成されますが、それを無効にするには、「終了時にダンプ」オプションを「n」に設定します (doe=n)。さらに、(プラットフォームに応じて) Ctrl-\ または Ctrl-Break を押すと、プロファイルが生成されます。Solaris OS および Linux の場合、QUIT シグナルが受信された (kill -QUIT pid) 場合にもプロファイルが生成されます。Ctrl-\ または Ctrl-Break を複数回押すと、1 つのファイルに複数のプロファイルが生成されます。
ほとんどの場合、出力にはトレース、スレッド、およびオブジェクトの ID が含まれています。それぞれのタイプの ID は通常、ほかの ID とは異なる番号から始まります。たとえば、トレースは 300000 から始まります。
次の出力は、一連の入力ファイルに対して Java コンパイラ (javac) を実行することで生成されたヒープ割り当てプロファイルです。ここでは、プロファイラ出力の一部のみを示します。
$ javac -J-agentlib:hprof=heap=sites Hello.java SITES BEGIN (ordered by live bytes) Wed Oct 4 13:13:42 2006 percent live alloc'ed stack class rank self accum bytes objs bytes objs trace name 1 44.13% 44.13% 1117360 13967 1117360 13967 301926 java.util.zip.ZipEntry 2 8.83% 52.95% 223472 13967 223472 13967 301927 com.sun.tools.javac.util.List 3 5.18% 58.13% 131088 1 131088 1 300996 byte[] 4 5.18% 63.31% 131088 1 131088 1 300995 com.sun.tools.javac.util.Name[]
ヒーププロファイル内の重要な情報は、プログラムのさまざまな部分で行われる割り当ての量です。上の SITES レコードは、合計容量の 44.13% が java.util.zip.ZipEntry オブジェクトに割り当てられたことを示しています。
割り当てサイトをソースコードに関連付けるには、ヒープ割り当ての原因となった動的スタックトレースを記録するのが早道です。次の出力は、プロファイラ出力の別の部分を示しています。この出力は、上記の出力で 4 つの割り当てサイトによって参照されていたスタックトレースを示しています。
TRACE 301926: java.util.zip.ZipEntry.<init>(ZipEntry.java:101) java.util.zip.ZipFile+3.nextElement(ZipFile.java:417) com.sun.tools.javac.jvm.ClassReader.openArchive(ClassReader.java:1374) com.sun.tools.javac.jvm.ClassReader.list(ClassReader.java:1631) TRACE 301927: com.sun.tools.javac.util.List.<init>(List.java:42) com.sun.tools.javac.util.List.<init>(List.java:50) com.sun.tools.javac.util.ListBuffer.append(ListBuffer.java:94) com.sun.tools.javac.jvm.ClassReader.openArchive(ClassReader.java:1374) TRACE 300996: com.sun.tools.javac.util.Name$Table.<init>(Name.java:379) com.sun.tools.javac.util.Name$Table.<init>(Name.java:481) com.sun.tools.javac.util.Name$Table.make(Name.java:332) com.sun.tools.javac.util.Name$Table.instance(Name.java:349) TRACE 300995: com.sun.tools.javac.util.Name$Table.<init>(Name.java:378) com.sun.tools.javac.util.Name$Table.<init>(Name.java:481) com.sun.tools.javac.util.Name$Table.make(Name.java:332) com.sun.tools.javac.util.Name$Table.instance(Name.java:349)
スタックトレースの各フレームには、クラス名、メソッド名、ソースファイル名、および行番号が含まれています。ユーザーは、HPROF エージェントによって収集されるフレームの最大数を設定できます。デフォルトの上限は 4 です。スタックトレースには、ヒープ割り当てを行なったメソッドだけでなく、最終的にメモリー割り当てが発生する呼び出しを行なったメソッドも表示されます。
ヒープダンプは heap=dump オプションを使って取得されます。ヒープダンプの形式は、format オプションの設定に応じて ASCII、バイナリのいずれかです。jhat (「2.5 jhat ユーティリティー」を参照) などのツールではバイナリ形式が使用されるので、format=b オプションが必要となります。バイナリ形式が指定された場合、プリミティブ型のインスタンスフィールドとプリミティブ配列の内容がダンプに含まれます。
次のコマンドでは、javac コンパイラを実行することでダンプが生成されます。
$ javac -J-agentlib:hprof=heap=dump Hello.java
出力は大きなファイルになります。これは、ガベージコレクタによって決定されたルートセットと、そのルートセットから到達可能なヒープ内の各 Java オブジェクトごとのエントリから構成されます。次は、サンプルヒープダンプから選択されたいくつかのレコードです。
HEAP DUMP BEGIN (39793 objects, 2628264 bytes) Wed Oct 4 13:54:03 2006 ROOT 50000114 (kind=<thread>, id=200002, trace=300000) ROOT 50000006 (kind=<JNI global ref>, id=8, trace=300000) ROOT 50008c6f (kind=<Java stack>, thread=200000, frame=5) : CLS 50000006 (name=java.lang.annotation.Annotation, trace=300000) loader 90000001 OBJ 50000114 (sz=96, trace=300001, class=java.lang.Thread@50000106) name 50000116 group 50008c6c contextClassLoader 50008c53 inheritedAccessControlContext 50008c79 blockerLock 50000115 OBJ 50008c6c (sz=48, trace=300000, class=java.lang.ThreadGroup@50000068) name 50008c7d threads 50008c7c groups 50008c7b ARR 50008c6f (sz=16, trace=300000, nelems=1, elem type=java.lang.String[]@5000008e) [0] 500007a5 CLS 5000008e (name=java.lang.String[], trace=300000) super 50000012 loader 90000001 : HEAP DUMP END
各レコードは、ルート、オブジェクトインスタンス、クラス、または配列を表す ROOT、OBJ、CLS、または ARR です。16 進数は、HPROF によって割り当てられた識別子です。これらの数値は、あるオブジェクトから別のオブジェクトへの参照を示すために使用されます。たとえば上のサンプルでは、java.lang.Thread インスタンス 50000114 が、スレッドグループ (50008c6c) やその他のオブジェクトへの参照を持っています。
一般に出力のサイズは非常に大きいので、ツールを使ってヒープダンプの出力を視覚化または処理する必要があります。そのようなツールの 1 つが jhat です。「2.5 jhat ユーティリティー」を参照してください。
HPROF ツールは、スレッドのサンプリングによって CPU 使用率の情報を収集できます。次は、javac コンパイラの実行から収集された出力の一部です。
$ javac -J-agentlib:hprof=cpu=samples Hello.java CPU SAMPLES BEGIN (total = 462) Wed Oct 4 13:33:07 2006 rank self accum count trace method 1 49.57% 49.57% 229 300187 java.util.zip.ZipFile.getNextEntry 2 6.93% 56.49% 32 300190 java.util.zip.ZipEntry.initFields 3 4.76% 61.26% 22 300122 java.lang.ClassLoader.defineClass2 4 2.81% 64.07% 13 300188 java.util.zip.ZipFile.freeEntry 5 1.95% 66.02% 9 300129 java.util.Vector.addElement 6 1.73% 67.75% 8 300124 java.util.zip.ZipFile.getEntry 7 1.52% 69.26% 7 300125 java.lang.ClassLoader.findBootstrapClass 8 0.87% 70.13% 4 300172 com.sun.tools.javac.main.JavaCompiler.<init> 9 0.65% 70.78% 3 300030 java.util.zip.ZipFile.open 10 0.65% 71.43% 3 300175 com.sun.tools.javac.main.JavaCompiler.<init> ... CPU SAMPLES END
HPROF エージェントは、実行中のすべてのスレッドのスタックを定期的にサンプリングし、もっとも頻繁にアクティブになるスタックトレースを記録します。上の count フィールドは、特定のスタックトレースがアクティブであることが見つかった回数を示しています。これらのスタックトレースは、アプリケーション内の CPU 使用率のホットスポットに相当します。
HPROF ツールは、CPU 使用率の情報を収集するために、あらゆるメソッドの入口と出口にコードを挿入し、それによって正確なメソッド呼び出し回数と各メソッド内で費やされた時間を追跡します。このプロセスではバイトコードインジェクション (BCI) が使用されるため、実行速度は cpu=samples オプションよりもかなり低下します。次は、javac コンパイラの実行から収集された出力の一部です。
$ javac -J-agentlib:hprof=cpu=times Hello.java CPU TIME (ms) BEGIN (total = 2082665289) Wed oct 4 13:43:42 2006 rank self accum count trace method 1 3.70% 3.70% 1 311243 com.sun.tools.javac.Main.compile 2 3.64% 7.34% 1 311242 com.sun.tools.javac.main.Main.compile 3 3.64% 10.97% 1 311241 com.sun.tools.javac.main.Main.compile 4 3.11% 14.08% 1 311173 com.sun.tools.javac.main.JavaCompiler.compile 5 2.54% 16.62% 8 306183 com.sun.tools.javac.jvm.ClassReader.listAll 6 2.53% 19.15% 36 306182 com.sun.tools.javac.jvm.ClassReader.list 7 2.03% 21.18% 1 307195 com.sun.tools.javac.comp.Enter.main 8 2.03% 23.21% 1 307194 com.sun.tools.javac.comp.Enter.complete 9 1.68% 24.90% 1 306392 com.sun.tools.javac.comp.Enter.classEnter 10 1.68% 26.58% 1 306388 com.sun.tools.javac.comp.Enter.classEnter ... CPU TIME (ms) END
この出力で、count はこのメソッドに入った本当の回数を表し、パーセンテージはこれらのメソッドで費やされたスレッド CPU 時間の尺度を表します。
Java VisualVM は、JDK ダウンロード (JDK release 7 update 7 以降) に含まれるツールの 1 つです。このツールは、Java アプリケーションの開発者が、アプリケーションのトラブルシューティングを行なったり、アプリケーションのパフォーマンスを監視および改善したりするのに役立ちます。Java VisualVM を使用すると、ヒープダンプの生成および解析、メモリーリークの特定、ガベージコレクションの実行およびモニター、およびメモリーと CPU の簡易プロファイリングの実行が可能になります。またこのツールは、チューニング、ヒープサイズ決定、オフライン分析、およびポストモーテム診断にも役立ちます。
さらに、Java VisualVM の機能を拡張する既存のプラグインを使用することもできます。たとえば、JConsole ツールのほとんどの機能が、MBeans タブおよび JConsole プラグインラッパータブを介して使用できます。標準 Java VisualVM プラグインのカタログから選択するには、Java VisualVM のメインウィンドウの「ツール」メニューから「プラグイン」を選択します。
Java VisualVM の包括的なドキュメントについては、http://download.oracle.com/javase/7/docs/technotes/guides/visualvm/index.html を参照してください
Java VisualVM では次のトラブルシューティングアクティビティーを実行できます。
ローカルおよびリモートの Java アプリケーションの一覧を表示します。
アプリケーションの構成や実行環境を表示します。このツールはアプリケーションごとに、次の基本的な実行時情報を表示します。PID、ホスト、main クラス、プロセスに渡された引数、JVM のバージョン、JDK ホーム、JVM フラグ、JVM 引数、システムプロパティー。
指定されたアプリケーションで OutOfMemoryError 例外が発生した場合のヒープダンプの作成を、有効および無効にします。
アプリケーションのメモリー消費、実行中のスレッド、ロード済みのクラスをモニターします。
ガベージコレクションをすぐにトリガーします。
ヒープダンプをすぐに作成します。その後、いくつかのビュー (サマリー、クラス別、インスタンス別) でヒープダンプを表示できます。また、ヒープダンプをローカルファイルシステムに保存することもできます。
アプリケーションパフォーマンスのプロファイリングやメモリー割り当ての分析を行います (ローカルアプリケーションの場合のみ)。また、プロファイルデータを保存することもできます。
スレッドダンプ (アプリケーションのアクティブスレッドのスタックトレース) をすぐに作成します。その後、スレッドダンプを表示できます。
コアダンプを分析します (Solaris OS および Linux の場合)。
アプリケーションスナップショットを取ることで、アプリケーションをオフラインで分析します。
コミュニティーから寄贈された追加プラグインを取得します。
独自プラグインの作成と共有を行います。
MBean を表示したり操作したりします (MBean タブプラグインのインストール後)。
Java VisualVM を起動するとメインのアプリケーションウィンドウが開き、ローカルマシン上で実行されている Java アプリケーションの一覧、接続されたすべてのリモートマシン上で実行されている Java アプリケーションの一覧、取得して保存されたすべての VM コアダンプの一覧 (Solaris OS および Linux の場合)、取得して保存されたすべてのアプリケーションスナップショットの一覧が表示されます。
Java VisualVM は、JDK 7 上で実行されている Java アプリケーションや、バージョン 5.0 上で正しいシステムプロパティーとともに起動された Java アプリケーションの JMX エージェントを自動的に検出して接続します。リモートマシン上のエージェントをツールが検出して接続するためには、リモートマシン上で jstatd デーモンが実行されている必要があります (「2.13 jstatd デーモン」を参照)。ターゲットアプリケーション内で実行されている JMX エージェントを Java VisualVM が自動的に発見して接続できない場合のために、ツールにはそれらの接続を明示的に作成する方法が用意されています。
JDK ダウンロードに含まれるもう 1 つの便利なツールは、JConsole モニタリングツールです。このツールは Java Management Extensions (JMX) に準拠しています。このツールは、Java 仮想マシンの組み込み JMX インストゥルメンテーションを使って、実行中のアプリケーションのパフォーマンスやリソース消費に関する情報を提供します。このツールは、JDK ダウンロードに含まれてはいますが、Java 実行環境で配備されたアプリケーションのモニターや管理にも使用できます。
JConsole ツールは、スレッド使用量やメモリー消費量などの有用な情報、およびクラスロード、実行時コンパイル、オペレーティングシステムに関する詳細情報を表示する目的で、任意の Java アプリケーションに接続できます。
この出力は、メモリーリーク、過剰なクラスロードや実行中のスレッドなどの問題の高レベルの診断に役立ちます。これは、チューニングやヒープサイズ決定にも役立つ可能性があります。
JConsole を使えば、モニタリングだけでなく、実行中のシステム内のいくつかのパラメータを動的に変更することもできます。たとえば、-verbose:gc オプションの設定を変更できるので、実行中のアプリケーションに対するガベージコレクションのトレース出力を動的に有効または無効にできます。
次のリストは、JConsole ツールを使ってモニターできるデータの概要を示しています。各見出しはツール内のタブペインに対応しています。
概要
このペインには、ヒープメモリー使用量、スレッド数、クラス数、および CPU 使用率の経時的推移を示すグラフが表示されます。この概要では、いくつかのリソースのアクティビティーを一度に視覚化できます。
メモリー
選択された 1 つのメモリー領域 (ヒープ、非ヒープ、各種メモリープール) について
メモリー使用量の経時的推移グラフ
現在のメモリーサイズ
コミットされたメモリーの量
メモリーの最大サイズ
ガベージコレクタの情報 (実行されたコレクションの回数や、ガベージコレクションの実行に費やされた合計時間など)。
現在使用中のヒープおよび非ヒープメモリーのパーセントを示すグラフ。
さらにこのペインでは、ガベージコレクションの実行を要求できます。
スレッド
スレッド使用量の経時的推移グラフ。
ライブスレッド - 現在のライブスレッド数。
ピーク - Java VM 起動後のライブスレッドの最大数。
選択された 1 つのスレッドの名前、状態、およびスタックトレース、およびブロックされたスレッドの場合は、スレッドが獲得を待機しているシンクロナイザとロックを所有しているスレッド。
「デッドロックの検出」ボタン - デッドロック検出を実行する要求をターゲットアプリケーションに送信し、各デッドロックサイクルをそれぞれ異なるタブに表示します。
クラス
ロード済みクラス数の経時的推移グラフ。
現在メモリーにロードされているクラスの数。
Java VM の起動後にメモリーにロードされたクラスの合計数 (そのあとアンロードされたものも含む)。
Java VM の起動後にメモリーからアンロードされたクラスの合計数。
VM サマリー
一般的な情報 (JConsole 接続データ、Java VM の稼働時間、Java VM で消費された CPU 時間、コンパイラ名や合計コンパイル時間など)。
スレッドおよびクラスのサマリー情報。
メモリーおよびガベージコレクションの情報 (ファイナライズ保留中のオブジェクトの数など)。
オペレーティングシステムに関する情報 (物理特性、実行中のプロセス用の仮想メモリー量、スワップ領域など)。
仮想マシン自体に関する情報 (引数やクラスパスなど)。
MBeans
このペインには、接続済みの JMX エージェントに登録されたすべてのプラットフォームおよびアプリケーション MBean を示すツリー構造が表示されます。ツリー内で MBean を選択すると、その属性、操作、通知、およびその他の情報が表示されます。
操作 (存在する場合) を呼び出せます。たとえば、com.sun.management ドメインに含まれる HotSpotDiagnostic MBean の操作 dumpHeap は、ヒープダンプを実行します。この操作の入力パラメータは、ターゲット VM が実行されているマシン上でのヒープダンプファイルのパス名になります。
もう 1 つの操作呼び出し例として、書き込み可能な属性の値を設定できます。たとえば、特定の VM フラグの値を設定、設定解除、または変更するには、HotSpotDiagnostic MBean の setVMOption 操作を呼び出します。フラグは、DiagnosticOptions 属性の値のリストによって示されます。
通知 (存在する場合) をサブスクライブするには、「サブスクライブ」および「サブスクライブ解除」ボタンを使用します。
JConsole は、ローカルアプリケーションとリモートアプリケーションの両方をモニターできます。接続先 JMX エージェントを指定する引数付きでツールを起動した場合、ツールは指定されたアプリケーションのモニタリングを自動的に開始します。
ローカルアプリケーションをモニターするには、コマンド jconsole pid (pid はアプリケーションのプロセス ID) を実行します。
リモートアプリケーションをモニターするには、コマンド jconsole hostname:portnumber (hostname はアプリケーションを実行しているホストの名前、portnumber は JMX エージェントの有効化時に指定したポート番号) を実行します。
jconsole コマンドを引数なしで実行した場合、ツールはまず、モニター対象のローカルまたはリモートプロセスを指定するための「新規接続」ウィンドウを表示します。「接続」メニューを使えば別のホストにいつでも接続できます。
JDK 1.5 リリースでは、-Dcom.sun.management.jmxremote オプションでモニター対象のアプリケーションを起動する必要があります。JDK 7 リリースでは、モニター対象アプリケーションを起動する際にオプションは不要です。
モニタリングツールの出力例として、次の画面はヒープメモリー使用量のグラフを示しています。
JConsole ツールの完全なチュートリアルは、このドキュメントの対象外です。ただし、次の各ドキュメントでは、モニタリングおよび管理機能や JConsole の使用方法について、より詳しく説明されています。
Java プラットフォームの監視と管理
http://download.oracle.com/javase/7/docs/technotes/guides/management/index.html
JMX を使用する監視と管理
http://download.oracle.com/javase/7/docs/technotes/guides/management/agent.html
jconsole の使用
http://download.oracle.com/javase/7/docs/technotes/guides/management/jconsole.html
jconsole のマニュアルページ
http://download.oracle.com/javase/7/docs/technotes/tools/share/jconsole.html
jdb ユーティリティーは、コマンド行デバッガの例として JDK リリースに含まれています。jdb ユーティリティーは、Java Debug Interface (JDI) を使ってターゲット VM を起動または接続します。$JAVA_HOME/demo/jpda/examples.jar には jdb のソースコードが含まれています。
Java Debug Interface (JDI) は、(通常はリモートの) 仮想マシンの実行状態にアクセスする必要があるデバッガや同様のシステムに役立つ情報を提供する、高レベルの Java API です。JDI は Java Platform Debugger Architecture (JPDA) のコンポーネントです。「2.17.5 Java Platform Debugger Architecture」を参照してください。
JDI のコネクタは、デバッガがターゲット仮想マシンに接続するための手段です。JDK リリースには従来、ターゲット VM とのデバッグセッションを開始して確立するコネクタと、(TCP/IP または共有メモリートランスポートを使用した) リモートデバッグに使用されるコネクタが付属していました。
JDK リリースにはさらに、Java 言語デバッガからクラッシュダンプやハングアッププロセスに接続することを許可する、Serviceability Agent (SA) コネクタもいくつか付属しています。これは、クラッシュまたはハングアップの際にアプリケーションが何をしていたのかを確認するのに役立つ可能性があります。
これらのコネクタは SACoreAttachingConnector、SADebugServerAttachingConnector、および SAPIDAttachingConnector です。
これらのコネクタは一般に、エンタープライズデバッガ (NetBeans IDE や商用 IDE など) で使用されます。次のサブセクションでは、jdb コマンド行デバッガでこれらのコネクタを使用する方法を示します。
コネクタの詳細については、http://download.oracle.com/javase/7/docs/technotes/guides/jpda/conninv.html#Connectors を参照してください。
コマンド jdb -listconnectors は、使用可能なコネクタの一覧を出力します。コマンド jdb -help は、コマンドの使用方法を出力します。
jdb ユーティリティーの詳細については、マニュアルページを参照してください。
Solaris OS および Linux: jdb のマニュアルページ
http://download.oracle.com/javase/7/docs/technotes/tools/solaris/jdb.html
Windows: jdb のマニュアルページ
http://download.oracle.com/javase/7/docs/technotes/tools/windows/jdb.html
この例では、SA PID 接続コネクタを使ってプロセスに接続します。ターゲットプロセスは、特別なオプションを付けて起動されません (つまり、-agentlib:jdwp オプションは不要です)。このコネクタがプロセスに接続する際は、読み取り専用モードで行われます。デバッガは、スレッドや実行中のアプリケーションを検査することはできますが、何かを変更することはできません。デバッガが接続されている間、プロセスは凍結されます。
次の例のコマンドは、sun.jvm.hotspot.jdi.SAPIDAttachingConnector という名前のコネクタを使用するように jdb に指示しています。これはクラス名ではなくコネクタ名です。コネクタは pid という名前の引数を 1 つ取ります (値はターゲットプロセスのプロセス ID、この例では 9302 です)。
$ jdb -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=9302 Initializing jdb ... > threads Group system: (java.lang.ref.Reference$ReferenceHandler)0xa Reference Handler unknown (java.lang.ref.Finalizer$FinalizerThread)0x9 Finalizer unknown (java.lang.Thread)0x8 Signal Dispatcher running (java.lang.Thread)0x7 Java2D Disposer unknown (java.lang.Thread)0x2 TimerQueue unknown Group main: (java.lang.Thread)0x6 AWT-XAWT running (java.lang.Thread)0x5 AWT-Shutdown unknown (java.awt.EventDispatchThread)0x4 AWT-EventQueue-0 unknown (java.lang.Thread)0x3 DestroyJavaVM running (sun.awt.image.ImageFetcher)0x1 Image Animator 0 sleeping (java.lang.Thread)0x0 Intro running > thread 0x7 Java2D Disposer[1] where [1] java.lang.Object.wait (native method) [2] java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:116) [3] java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:132) [4] sun.java2d.Disposer.run (Disposer.java:125) [5] java.lang.Thread.run (Thread.java:619) Java2D Disposer[1] up 1 Java2D Disposer[2] where [2] java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:116) [3] java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:132) [4] sun.java2d.Disposer.run (Disposer.java:125) [5] java.lang.Thread.run (Thread.java:619)
この例では、threads コマンドを使ってすべてのスレッドの一覧を取得しています。 次に、thread 0x7 コマンドで特定のスレッドを選択し、where コマンドを使ってスレッドダンプを取得しています。次に、up 1 コマンドを使ってスタック内で 1 フレーム上に移動し、where コマンドを再度使ってスレッドダンプを取得しています。
デバッガをコアファイルに接続するには、SA コア接続コネクタを使用します。コアファイルはクラッシュ後に作成された可能性があります (「第 4 章「システムクラッシュのトラブルシューティング」を参照)。コアファイルは、Solaris OS では gcore コマンド、Linux では gdb の gcore コマンドを使って取得することもできます。コアファイルはコアファイル作成時のプロセスのスナップショットなので、コネクタは読み取り専用モードで接続します。デバッガは、クラッシュ時のスレッドや実行中アプリケーションを検査できます。
次のコマンドは、このコネクタの使用例です。
$ jdb -connect sun.jvm.hotspot.jdi.SACoreAttachingConnector:\ javaExecutable=$JAVA_HOME/bin/java,core=core.20441
このコマンドは、sun.jvm.hotspot.jdi.SACoreAttachingConnector という名前のコネクタを使用するように jdb に指示しています。このコネクタは javaExecutable、core という名前の 2 つの引数を取ります。javaExecutable 引数は Java バイナリの名前を示します。core 引数は、コアファイル名 (この例では PID 20441 のプロセスからのコア) です。
別のマシンからトランスポートされたコアファイルをデバッグするには、OS のバージョンとライブラリが一致する必要があります。この場合はまず、SA デバッグサーバーという名前のプロキシサーバーを実行できます。その後、デバッガがインストールされたマシン上で、SA デバッグサーバー接続コネクタを使ってデバッグサーバーに接続できます。
下の例ではマシン 1、マシン 2 という 2 つのマシンがあります。マシン 1 でコアファイルが使用可能、マシン 2 でデバッガが使用可能です。次のようにマシン 1 で SA デバッグサーバーが起動されます。
$ jsadebugd $JAVA_HOME/bin/java core.20441
jsadebugd コマンドは 2 つの引数を取ります。最初の引数は実行可能ファイルの名前です。これはほとんどの場合 java ですが、別の名前のこともあります (埋め込み VM の場合など)。2 番目の引数は、コアファイルの名前です。この例でのコアファイルは、gcore ユーティリティーを使って PID 20441 のプロセスに対して取得されました。
マシン 2 上で次のコマンドのようにして、SA デバッグサーバー接続コネクタを使ってデバッガはリモートの SA デバッグサーバーに接続します。
$ jdb -connect sun.jvm.hotspot.jdi.SADebugServerAttachingConnector:\ debugServerName=machine1
このコマンドは、sun.jvm.hotspot.jdi.SADebugServerAttachingConnector という名前のコネクタを使用するように jdb に指示しています。このコネクタは、debugServerName という引数 (SA デバッグサーバーが実行されているマシンのホスト名または IP アドレス) を 1 つ持ちます。
SA デバッグサーバーはハングアッププロセスのリモートデバッグにも使用できます。その場合、単一の引数 (プロセスのプロセス ID) を取ります。さらに、同じマシン上で複数のデバッグサーバーを実行する必要がある場合は、それぞれに一意の ID を指定する必要があります。SA デバッグサーバー接続コネクタで、この ID は追加のコネクタ引数として提供されます。これらの詳細については、JPDA のドキュメントを参照してください。
jhat ツールは、ヒープスナップショット内のオブジェクトトポロジを参照するための便利な手段を提供します。このツールは、Heap Analysis Tool (HAT) を置き換えるために JDK 6 リリースで導入されました。
jhat ユーティリティーの詳細については、jhat (Java Heap Analysis Tool) のマニュアルページを参照してください。
このツールは、バイナリ形式のヒープダンプ (jmap -dump で生成されたヒープダンプなど) を解析します。
このユーティリティーは、意図しないオブジェクト保持のデバッグに役立つ可能性があります。この用語は、もう不要になっているけれども、ルートセットからの一部のパス経由での参照のためにライブに保たれているオブジェクトを記述するために使用されます。これは、オブジェクトが不要になったあともオブジェクトへの意図しない静的参照が残っている場合、Observer や Listener で不要になった対象からの登録解除が失敗した場合、オブジェクトを参照している Thread が終了すべきときに終了しない場合などに発生することがあります。意図しないオブジェクト保持は、Java 言語ではメモリーリークに相当します。
このツールには標準クエリーがいくつか用意されています。たとえば、ルートセットから指定されたオブジェクトまでのすべての参照パスを表示する「ルート」クエリーは、不要なオブジェクト保持の発見に特に役立ちます。
標準クエリーに加え、Object Query Language (OQL) インタフェースで独自のカスタムクエリーを開発することもできます。
jhat コマンドを発行すると、このユーティリティーにより、指定された TCP ポート上で HTTP サーバーが起動されます。その後、任意のブラウザを使ってサーバーに接続し、指定したヒープダンプに対してクエリーを実行できます。
次の例では、jhat を実行して snapshot.hprof という名前のヒープダンプファイルを分析する方法を示しています。
$ jhat snapshot.hprof Started HTTP server on port 7000 Reading from java_pid2278.hprof... Dump file created Fri May 19 17:18:38 BST 2006 Snapshot read, resolving... Resolving 6162194 objects... Chasing references, expect 12324 dots................................ Eliminating duplicate references..................................... Snapshot resolved. Server is ready.
この時点で jhat により、ポート 7000 上で HTTP サーバーが起動されています。ブラウザを http://localhost:7000 にポイントして jhat サーバーに接続します。
サーバーに接続されたら、標準クエリー (次のサブセクションを参照) を実行したり、OQL クエリー (「2.5.2 カスタムクエリー」を参照) を作成したりできます。「すべてのクラス」クエリーがデフォルトで表示されます。
これらのサブセクションでは標準クエリーについて説明します。
デフォルトページの「すべてのクラス」クエリーには、ヒープ内に存在しているすべてのクラス (プラットフォームクラスは除く) が表示されます。このリストは完全修飾クラス名でソートされ、パッケージ別に分けられます。クラスの名前をクリックすると、「クラス」クエリーに移動します。
このクエリーの 2 番目のバリアントには、プラットフォームクラスが含まれます。プラットフォームクラスには、完全修飾名が java、sun.、javax.swing.、char[ などの接頭辞で始まるクラスが含まれます。この接頭辞のリストは、resources/platform_names.txt という名前のシステムリソースファイルに含まれています。このリストをオーバーライドするには、JAR ファイル内でリストを置き換えるか、または jhat の呼び出し時にクラスパス上で置換が先に発生するように調整してください。
「クラス」クエリーには、クラスに関する情報が表示されます。これにはスーパークラス、サブクラス、インスタンスデータメンバー、静的データメンバーが含まれます。このページから、任意の参照先クラスに移動したり、「インスタンス」クエリーに移動したりできます。
「オブジェクト」クエリーでは、ヒープ上にあったオブジェクトに関する情報が提供されます。ここから、オブジェクトのクラスに移動したり、オブジェクトのオブジェクトメンバーの値に移動したりできます。現在のオブジェクトを参照しているオブジェクトに移動することもできます。おそらくもっとも価値の高いクエリーは、末尾にある「ルート」クエリー (「ルートセットからの参照チェーン」) です。
オブジェクトクエリーでは、オブジェクト割り当て時点でのスタックバックトレースも提供されます。
インスタンスクエリーでは、特定のクラスのすべてのインスタンスが表示されます。allInstances バリアントには、特定クラスのサブクラスのインスタンスも含まれます。ここから、元のクラスに戻ったり、いずれかのインスタンスの「オブジェクト」クエリーに移動したりできます。
「ルート」クエリーには、ルートセットから特定のオブジェクトまでの参照チェーンが表示されます。これは、特定のオブジェクトに到達可能なルートセットの各メンバーに 1 つのチェーンを提供します。このツールではこれらのチェーンの計算時に深さ優先探索が行われるため、最小長の参照チェーンが提供されます。
「ルート」クエリーには 2 つの種類があります。弱参照を除外するもの (「ルート」) と弱参照を含めるもの (「すべてのルート」) です。弱参照とは、その参照先がファイナライズ可能となり、ファイナライズされてから回収されるのを妨げない参照オブジェクトのことです。弱参照だけから参照されているオブジェクトは通常、保持されているとはみなされません (その領域が必要になった時点ですぐにガベージコレクタが収集できるため)。
これはおそらく、意図しないオブジェクト保持をデバッグするうえで、jhat でもっとも価値の高いクエリーです。保持されているオブジェクトを見つけたら、このクエリーで保持されている理由がわかります。
このクエリーは「オブジェクト」クエリーからアクセス可能であり、特定のオブジェクトから到達可能なすべてのオブジェクトの推移閉包を表示します。このリストはサイズの降順 (各サイズ内ではアルファベット順) にソートされます。末尾には、すべての到達可能オブジェクトの合計サイズが表示されます。これは、少なくともオブジェクトのトポロジが単純なシステムでは、メモリー内でのオブジェクトの実行時フットプリント合計を決定する際に役立つ可能性があります。
このクエリーの価値がもっとも高まるのは、-exclude コマンド行オプションと組み合わせて使用する場合です。これは、分析対象のオブジェクトが Observable である場合などに役立ちます。デフォルトではすべての Observer が到達可能となり、それらが合計サイズにカウントされます。-exclude オプションを使えば、データメンバー java.util.Observable.obs と java.util.Observable.arr を除外できます。
このクエリーでは、システム内のプラットフォームクラス以外のすべてのクラスのインスタンス数が表示されます。そのソート順は、インスタンス数の降順になります。意図しないオブジェクト保持の問題を突きとめる良い方法は、プログラムをさまざまな入力で長時間実行したあと、ヒープダンプを要求することです。すべてのクラスのインスタンス数を確認すると、インスタンス数が予想より多いクラスがいくつかあることに気づく場合があります。その場合はそれらを (おそらく「ルート」クエリーを使って) 分析し、保持されている理由を確認できます。このクエリーのバリアントには、プラットフォームクラスが含まれます。
プラットフォームクラスについては、「すべてのクラス」クエリーのセクションで定義しています。
このクエリーでは、弱参照を含むルートセットのすべてのメンバーが表示されます。
「新規インスタンス」クエリーは、2 つのヒープダンプ付きで jhat サーバーを呼び出した場合にのみ利用できます。このクエリーは「インスタンス」クエリーに似ていますが、新しいインスタンスだけを表示する点が異なります。インスタンスは、それが 2 番目のヒープダンプ内にあり、かつ同じ ID を持つ同じ型のオブジェクトがベースラインヒープダンプ内にない場合に、新規とみなされます。オブジェクトの ID は、オブジェクトを一意に識別する 32 ビットまたは 64 ビットの整数です。
組み込みヒストグラムおよびファイナライザヒストグラムクエリーも、役立つ情報を提供します。
組み込み Object Query Language (OQL) インタフェースで独自のカスタムクエリーを開発できます。最初のページの「OQL クエリーの実行」ボタンをクリックすると OQL クエリーページが表示され、そこでカスタムクエリーを作成して実行できます。OQL のヘルプ機能では、組み込み関数が例付きで説明されます。
select 文の構文は次のとおりです。
select JavaScript-expression-to-select [ from [instanceof] classname identifier [ where JavaScript-boolean-expression-to-filter ] ]
次は select 文の例です。
select s from java.lang.String s where s.count >= 100
jhat から有用な情報を得るには、アプリケーションのある程度の知識に加え、そこで使用されているライブラリや API に関するある程度の知識も要求されることがよくあります。とはいえ、一般にこのツールを使えば、次の 2 つの重要な質問に答えることができます。
何がオブジェクトをライブに保っているのか。
このオブジェクトはどこで割り当てられたのか。
オブジェクトインスタンスを表示しているときに、「このオブジェクトへの参照」というタイトルのセクションに一覧表示されているオブジェクトをチェックすれば、このオブジェクトを直接参照しているオブジェクトを確認できます。より重要なことは、「ルート」クエリーを使ってルートセットから特定のオブジェクトまでの参照チェーンを確認することです。これらの参照チェーンは、ルートオブジェクトからこのオブジェクトへのパスを示します。これらのチェーンがあれば、どのようにすればルートセットからオブジェクトに到達できるかをすばやく確認できます。
前述したように、「ルート」クエリーには 2 つの種類があります。弱参照を除外するもの (「ルート」) と弱参照を含めるもの (「すべてのルート」) です。弱参照とは、その参照先がファイナライズ可能となり、ファイナライズされてから回収されるのを妨げない参照オブジェクトのことです。弱参照だけから参照されているオブジェクトは通常、保持されているとはみなされません (その領域が必要になった時点ですぐにガベージコレクタが収集できるため)。
jhat ツールは、ルートセット参照チェーンを、ルートの種類に基づいて次の順序でソートします。
Java クラスの静的データメンバー。
Java ローカル変数。これらのルートでは、それらの担当のスレッドが表示されます。Thread は Java オブジェクトなので、このリンクはクリック可能です。このため、たとえばスレッドの名前に容易に移動できます。
ネイティブの静的な値。
ネイティブのローカル変数。ここでも、そのようなルートはそれらのスレッドで識別されます。
オブジェクトインスタンスが表示されている場合、オブジェクトの割り当て元を示すタイトルのセクションに、スタックトレースの形式で割り当てサイトが表示されます。このようにして、オブジェクトがどこで作成されたかを確認できます。
この割り当てサイトの情報は、HPROF で heap=all オプションを使用してヒープダンプが作成された場合にのみ入手できます。この HPROF オプションには heap=dump オプションと heap=sites オプションの両方が含まれます。
単一のオブジェクトダンプを使ってリークを特定できない場合の別のアプローチは、一連のダンプを収集し、各ダンプの合間に作成されたオブジェクトに焦点を絞ることです。jhat ツールでは -baseline オプションを使ってこの機能が提供されています。
2 つのダンプが HPROF によって同じ VM インスタンスから生成された場合、-baseline オプションを使えばそれらを比較できます。同じオブジェクトがどちらのダンプにも現れる場合、それは報告される新規オブジェクトのリストから除外されます。1 つのダンプがベースラインとして指定され、そのベースラインの取得以降に 2 番目のダンプで作成されたオブジェクトに分析を集中できます。
次の例ではベースラインの指定方法を示します。
$ jhat -baseline snapshot.hprof#1 snapshot.hprof#2
上の例では、2 つのダンプはファイル snapshot.hprof 内にあり、それらは #1 と #2 をファイル名の末尾に付加することによって区別されます。
2 つのヒープダンプ付きで jhat が起動された場合、「すべてのクラスのインスタンス数」クエリーに、その型の新規オブジェクト数である追加列が含まれます。インスタンスは、それが 2 番目のヒープダンプ内にあり、かつ同じ ID を持つ同じ型のオブジェクトがベースライン内にない場合に、新規とみなされます。新規カウントをクリックすると、jhat はその型の新しいオブジェクトを一覧表示します。このときインスタンスごとに、それがどこで割り当てられたか、これらの新規オブジェクトが参照しているオブジェクト、および新規オブジェクトを参照しているほかのオブジェクトを表示できます。
一般に、-baseline オプションは、識別する必要のあるオブジェクトが連続するダンプの合間に作成されたに、非常に役立つ可能性があります。
jinfo コマンド行ユーティリティーは、実行中の Java プロセスまたはクラッシュダンプから構成情報を取得し、仮想マシンの起動時に使用されたシステムプロパティーまたはコマンド行フラグを出力します。
このユーティリティーは、jsadebugd デーモンを使ってリモートマシン上のプロセスやコアファイルを照会することもできます。この場合、出力により長い時間がかかります。
このユーティリティーの -flag オプションを使えば、指定された Java プロセスの特定の Java VM フラグの値を動的に設定、設定解除、または変更できます。「B.1.1 フラグ値の動的変更」を参照してください。
jinfo ユーティリティーの詳細については、マニュアルページを参照してください。
次は、Java プロセスからの出力例です。
$ jinfo 29620 Attaching to process ID 29620, please wait... Debugger attached successfully. Client compiler detected. JVM version is 1.6.0-rc-b100 Java System Properties: java.runtime.name = Java(TM) SE Runtime Environment sun.boot.library.path = /usr/jdk/instances/jdk1.6.0/jre/lib/sparc java.vm.version = 1.6.0-rc-b100 java.vm.vendor = Sun Microsystems Inc. java.vendor.url = http://java.sun.com/ path.separator = : java.vm.name = Java HotSpot(TM) Client VM file.encoding.pkg = sun.io sun.java.launcher = SUN_STANDARD sun.os.patch.level = unknown java.vm.specification.name = Java Virtual Machine Specification user.dir = /home/js159705 java.runtime.version = 1.6.0-rc-b100 java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment java.endorsed.dirs = /usr/jdk/instances/jdk1.6.0/jre/lib/endorsed os.arch = sparc java.io.tmpdir = /var/tmp/ line.separator = java.vm.specification.vendor = Sun Microsystems Inc. os.name = SunOS sun.jnu.encoding = ISO646-US java.library.path = /usr/jdk/instances/jdk1.6.0/jre/lib/sparc/client:/usr/jdk/instances/jdk1.6.0/jre/lib/sparc: /usr/jdk/instances/jdk1.6.0/jre/../lib/sparc:/net/gtee.sfbay/usr/sge/sge6/lib/sol-sparc64: /usr/jdk/packages/lib/sparc:/lib:/usr/lib java.specification.name = Java Platform API Specification java.class.version = 50.0 sun.management.compiler = HotSpot Client Compiler os.version = 5.10 user.home = /home/js159705 user.timezone = US/Pacific java.awt.printerjob = sun.print.PSPrinterJob file.encoding = ISO646-US java.specification.version = 1.6 java.class.path = /usr/jdk/jdk1.6.0/demo/jfc/Java2D/Java2Demo.jar user.name = js159705 java.vm.specification.version = 1.0 java.home = /usr/jdk/instances/jdk1.6.0/jre sun.arch.data.model = 32 user.language = en java.specification.vendor = Sun Microsystems Inc. java.vm.info = mixed mode, sharing java.version = 1.6.0-rc java.ext.dirs = /usr/jdk/instances/jdk1.6.0/jre/lib/ext:/usr/jdk/packages/lib/ext sun.boot.class.path = /usr/jdk/instances/jdk1.6.0/jre/lib/resources.jar: /usr/jdk/instances/jdk1.6.0/jre/lib/rt.jar:/usr/jdk/instances/jdk1.6.0/jre/lib/sunrsasign.jar: /usr/jdk/instances/jdk1.6.0/jre/lib/jsse.jar: /usr/jdk/instances/jdk1.6.0/jre/lib/jce.jar:/usr/jdk/instances/jdk1.6.0/jre/lib/charsets.jar: /usr/jdk/instances/jdk1.6.0/jre/classes java.vendor = Sun Microsystems Inc. file.separator = / java.vendor.url.bug = http://java.sun.com/cgi-bin/bugreport.cgi sun.io.unicode.encoding = UnicodeBig sun.cpu.endian = big sun.cpu.isalist = VM Flags:
ターゲット Java VM を -classpath および -Xbootclasspath 引数付きで起動した場合、jinfo の出力に java.class.path と sun.boot.class.path の設定が含まれます。この情報は、クラスローダーの問題を調査する際に必要となる可能性があります。
jinfo ツールは、プロセスから情報を取得するだけでなく、コアファイルを入力として使用できます。Solaris OS の場合、たとえば gcore ユーティリティーを使えば、上の例のプロセスのコアファイルを取得できます。コアファイルの名前は core.29620 となり、プロセスの作業ディレクトリ内に生成されます。次の例のように、Java 実行可能ファイルとコアファイルへのパスを、jinfo ユーティリティーの引数として指定する必要があります。
$ jinfo $JAVA_HOME/bin/java core.29620
バイナリ名は java でないことがあります。これは、JNI 呼び出し API を使って VM が作成されたときに発生します。jinfo ツールでは、コアファイルの生成元のバイナリが必要です。
jmap コマンド行ユーティリティーは、実行中の VM またはコアファイルのメモリー関係の統計を出力します。
このユーティリティーは、jsadebugd デーモンを使ってリモートマシン上のプロセスやコアファイルを照会することもできます。この場合、出力により長い時間がかかります。
プロセスまたはコアファイルでコマンド行オプションなしで jmap を使用された場合、ロードされた共有オブジェクトの一覧が出力されます (出力は Solaris OS の pmap ユーティリティーに似ています)。より詳細な情報を得るために、オプション -heap、-histo、または -permstat を使用できます。これらのオプションについては、以降の各サブセクションで説明します。
さらに、JDK 7 リリースで導入された -dump:format=b,file=filename オプションを使用すると、jmap によって、Java ヒープがバイナリ HPROF 形式で指定されたファイルにダンプされます。その後、jhat ツールを使ってこのファイルを解析できます。
jmap pid コマンドがハングアッププロセスのために応答しない場合、-F オプション (Solaris OS および Linux の場合のみ) を使って Serviceability Agent の使用を強制できます。
jmap ユーティリティーの詳細については、マニュアルページを参照してください。
次の Java ヒープ情報を取得するために、-heap オプションが使用されます。
ガベージコレクション (GC) アルゴリズムに固有の情報 (GC アルゴリズムの名前 (Parallel GC など) やアルゴリズム固有の詳細 (Parallel GC のスレッド数など) が含まれます)。
ヒープの構成。ヒープの構成は、コマンド行オプションとして指定されたか、またはマシンの構成に基づいて VM によって選択された可能性があります。
ヒープ使用量のサマリー。ツールは世代 (ヒープの領域) ごとに、ヒープの合計容量、使用中のメモリー、および使用可能な空きメモリーを出力します。領域の集まりとして構成された世代 (New 世代など) では、領域単位のメモリーサイズサマリーが含まれます。
次の例は、jmap -heap コマンドの出力を示しています。
$ jmap -heap 29620 Attaching to process ID 29620, please wait... Debugger attached successfully. Client compiler detected. JVM version is 1.6.0-rc-b100 using thread-local object allocation. Mark Sweep Compact GC Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 67108864 (64.0MB) NewSize = 2228224 (2.125MB) MaxNewSize = 4294901760 (4095.9375MB) OldSize = 4194304 (4.0MB) NewRatio = 8 SurvivorRatio = 8 PermSize = 12582912 (12.0MB) MaxPermSize = 67108864 (64.0MB) Heap Usage: New Generation (Eden + 1 Survivor Space): capacity = 2031616 (1.9375MB) used = 70984 (0.06769561767578125MB) free = 1960632 (1.8698043823242188MB) 3.4939673639112905% used Eden Space: capacity = 1835008 (1.75MB) used = 36152 (0.03447723388671875MB) free = 1798856 (1.7155227661132812MB) 1.9701276506696428% used From Space: capacity = 196608 (0.1875MB) used = 34832 (0.0332183837890625MB) free = 161776 (0.1542816162109375MB) 17.716471354166668% used To Space: capacity = 196608 (0.1875MB) used = 0 (0.0MB) free = 196608 (0.1875MB) 0.0% used tenured generation: capacity = 15966208 (15.2265625MB) used = 9577760 (9.134063720703125MB) free = 6388448 (6.092498779296875MB) 59.98769400974859% used Perm Generation: capacity = 12582912 (12.0MB) used = 1469408 (1.401336669921875MB) free = 11113504 (10.598663330078125MB) 11.677805582682291% used
-histo オプションを使って、クラス単位のヒープヒストグラムを取得できます。
実行中のプロセスに対してコマンドが実行されると、ツールはクラスごとにオブジェクト数、メモリーサイズ (バイト)、および完全修飾クラス名を出力します。HotSpot VM の内部クラスは山括弧で囲まれます。ヒストグラムは、ヒープがどのように使われているかを理解するのに役立ちます。オブジェクトのサイズを得るには、合計サイズをそのオブジェクト型の数で割る必要があります。
次の例は、jmap -histo コマンドをプロセスに対して実行した場合の出力を示しています。
$ jmap -histo 29620 num #instances #bytes class name -------------------------------------- 1: 1414 6013016 [I 2: 793 482888 [B 3: 2502 334928 <constMethodKlass> 4: 280 274976 <instanceKlassKlass> 5: 324 227152 [D 6: 2502 200896 <methodKlass> 7: 2094 187496 [C 8: 280 172248 <constantPoolKlass> 9: 3767 139000 [Ljava.lang.Object; 10: 260 122416 <constantPoolCacheKlass> 11: 3304 112864 <symbolKlass> 12: 160 72960 java2d.Tools$3 13: 192 61440 <objArrayKlassKlass> 14: 219 55640 [F 15: 2114 50736 java.lang.String 16: 2079 49896 java.util.HashMap$Entry 17: 528 48344 [S 18: 1940 46560 java.util.Hashtable$Entry 19: 481 46176 java.lang.Class 20: 92 43424 javax.swing.plaf.metal.MetalScrollButton ... more lines removed here to reduce output... 1118: 1 8 java.util.Hashtable$EmptyIterator 1119: 1 8 sun.java2d.pipe.SolidTextRenderer Total 61297 10152040
コアファイルに対して jmap -histo コマンドが実行された場合、ツールはクラスごとにサイズ、カウント、クラス名を出力します。HotSpot VM の内部クラスには、先頭にアスタリスク (*) が付けられます。
& jmap -histo /net/koori.sfbay/onestop/jdk/6.0/promoted/all/b100/binaries/ solaris-sparcv9/bin/java core Attaching to core core from executable /net/koori.sfbay/onestop/jdk/6.0/ promoted/all/b100/binaries/solaris-sparcv9/bin/java, please wait... Debugger attached successfully. Server compiler detected. JVM version is 1.6.0-rc-b100 Iterating over heap. This may take a while... Heap traversal took 8.902 seconds. Object Histogram: Size Count Class description ------------------------------------------------------- 4151816 2941 int[] 2997816 26403 * ConstMethodKlass 2118728 26403 * MethodKlass 1613184 39750 * SymbolKlass 1268896 2011 * ConstantPoolKlass 1097040 2011 * InstanceKlassKlass 882048 1906 * ConstantPoolCacheKlass 758424 7572 char[] 733776 2518 byte[] 252240 3260 short[] 214944 2239 java.lang.Class 177448 3341 * System ObjArray 176832 7368 java.lang.String 137792 3756 java.lang.Object[] 121744 74 long[] 72960 160 java2d.Tools$3 63680 199 * ObjArrayKlassKlass 53264 158 float[] ... more lines removed here to reduce output...
Permanent 世代は、仮想マシン自体のすべてのリフレクションデータ (クラスオブジェクトやメソッドオブジェクトなど) を保持するヒープ領域です (Java 仮想マシン仕様では「メソッド領域」とも呼ばれる)。
非常に多くのクラスを動的に生成してロードするアプリケーション (Java Server Pages や Web コンテナなど) では、Permanent 世代のサイズの構成が重要になる可能性があります。アプリケーションでロードされたクラスが「多すぎた」場合、OutOfMemoryError でアプリケーションが異常終了する可能性があります。具体的なエラーは、Exception in thread XXXX java.lang.OutOfMemoryError: PermGen space です。OutOfMemoryError のこれの理由およびその他の理由については、「3.1 OutOfMemoryError の意味」を参照してください。
Permanent 世代に関する詳細情報を得るために、-permstat オプションを使って Permanent 世代内のオブジェクトの統計を出力できます。次の例は、jmap -permstat コマンドの出力を示しています。
$ jmap -permstat 29620 Attaching to process ID 29620, please wait... Debugger attached successfully. Client compiler detected. JVM version is 1.6.0-rc-b100 12674 intern Strings occupying 1082616 bytes. finding class loader instances ..Unknown oop at 0xd0400900 Oop's klass is 0xd0bf8408 Unknown oop at 0xd0401100 Oop's klass is null done. computing per loader stat ..done. please wait.. computing liveness.........................................done. class_loader classes bytes parent_loader alive? type <bootstrap> 1846 5321080 null live <internal> 0xd0bf3828 0 0 null live sun/misc/Launcher$ExtClassLoader@0xd8c98c78 0xd0d2f370 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0c99280 1 1440 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0b71d90 0 0 0xd0b5b9c0 live java/util/ResourceBundle$RBClassLoader@0xd8d042e8 0xd0d2f4c0 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0b5bf98 1 920 0xd0b5bf38 dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0c99248 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0d2f488 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0b5bf38 6 11832 0xd0b5b9c0 dead sun/reflect/misc/MethodUtil@0xd8e8e560 0xd0d2f338 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0d2f418 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0d2f3a8 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0b5b9c0 317 1397448 0xd0bf3828 live sun/misc/Launcher$AppClassLoader@0xd8cb83d8 0xd0d2f300 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0d2f3e0 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0ec3968 1 1440 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0e0a248 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0c99210 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0d2f450 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0d2f4f8 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 0xd0e0a280 1 904 null dead sun/reflect/DelegatingClassLoader@0xd8c22f50 total = 22 2186 6746816 N/A alive=4, dead=18 N/A
クラスローダーオブジェクトごとに次の詳細が出力されます。
クラスローダーオブジェクトのアドレス (ユーティリティーが実行されたスナップショット時点)
ロードされたクラスの数
このクラスローダーによってロードされたすべてのクラスのメタデータで消費されているバイト数 (概算)
親クラスローダー (存在する場合) のアドレス
ローダーオブジェクトが将来ガベージコレクトされるかどうかの「live」または「dead」マーク
このクラスローダーのクラス名
jps ユーティリティーは、ターゲットシステム上の現在のユーザーで計測された HotSpot 仮想マシンを一覧表示します。VM が埋め込まれた環境、つまり java 起動ツールではなく JNI 呼び出し API を使って VM が起動される環境では、このユーティリティーが非常に役立ちます。こうした環境では、プロセスリスト内で Java プロセスを認識するのが常に容易であるとはかぎりません。
次の例は、jps ユーティリティーの使用方法を示しています。
$ jps 16217 MyApplication 16342 jps
このユーティリティーは、ユーザーがアクセス権を持つ仮想マシンを一覧表示します。これは、オペレーティングシステム固有のアクセス制御メカニズムによって決定されます。Solaris OS 上でたとえば非 root ユーザーが jps ユーティリティーを実行した場合、出力はそのユーザーの uid で起動された仮想マシンの一覧です。
このユーティリティーには、プロセス ID の一覧表示に加え、アプリケーションの main メソッドに渡された引数、VM 引数の完全なリスト、アプリケーションの main クラスの完全なパッケージ名を出力するオプションも用意されています。また、リモートシステム上で jstat デーモン (jstatd) が実行されていれば、jps ユーティリティーはリモートシステム上のプロセスを一覧表示することもできます。
システム上でいくつかの Java Web Start アプリケーションを実行している場合、次の例に示すように、それらは同じに見えてしまいがちです。
$ jps 1271 jps 1269 Main 1190 Main
この場合、次のように jps -m を使ってそれらを区別します。
$ jps -m 1271 jps -m 1269 Main http://bugster.central.sun.com/bugster.jnlp 1190 Main http://webbugs.sfbay/IncidentManager/incident.jnlp
jps ユーティリティーの詳細については、マニュアルページを参照してください。
このユーティリティーは、Sun がサポートするすべてのオペレーティングシステムプラットフォームの JDK ダウンロードに含まれています。
注 - Windows 98 や Windows ME では HotSpot インストゥルメンテーションにアクセスできません。さらに、Windows で一時ディレクトリが FAT32 ファイルシステム上にある場合、インストゥルメンテーションにアクセスできない可能性があります。
jrunscript ユーティリティーはコマンド行スクリプトシェルです。インタラクティブモード、バッチモードの両方のスクリプト実行がサポートされています。このシェルではデフォルトで JavaScript が使用されますが、ほかの任意のスクリプト言語を指定し、そのスクリプトエンジンの .class ファイル を含む JAR ファイルへのパスを指定できます。
jrunscript ユーティリティーでは、Java 言語とスクリプト言語間で通信が行われるため、探求的なプログラミングスタイルがサポートされています。
jrunscript ユーティリティーの詳細については、マニュアルページを参照してください。
Serviceability Agent Debug Daemon (jsadebugd) は、Java プロセスまたはコアファイルに接続し、デバッグサーバーとして動作します。このユーティリティーは現在のところ、Solaris OS と Linux でのみ利用できます。jstack、jmap および jinfo などのリモートクライアントは、Java Remote Method Invocation (RMI) を使用しているサーバーに接続できます。
jsadebugd ユーティリティーの詳細については、マニュアルページを参照してください。
jstack コマンド行ユーティリティーは、指定されたプロセスまたはコアファイルに接続し、仮想マシンに接続されたすべてのスレッド (Java スレッドや VM 内部スレッドも含む) のスタックトレースを出力するほか、オプションでネイティブスタックフレームも出力します。このユーティリティーではデッドロック検出も実行されます。
このユーティリティーは、jsadebugd デーモンを使ってリモートマシン上のプロセスやコアファイルを照会することもできます。この場合、出力により長い時間がかかります。
すべてのスレッドのスタックトレースは、デッドロックやハングアップなど、いくつかの問題の診断に役立つ可能性があります。
JDK 7 リリースで導入された -l オプションは、所有可能なシンクロナイザをヒープ内で探して java.util.concurrent.locks に関する情報を出力するように、ユーティリティーに指示します。このオプションがない場合、スレッドダンプにはモニターの情報のみが含まれます。
JDK 7 以降、jstack pid オプションの出力は、アプリケーションコンソール (標準入力) で Ctrl-\ を押すかプロセスに QUIT シグナルを送信した場合に得られるものと同じです。出力例については、「2.15 Ctrl-Break ハンドラ」を参照してください。
スレッドダンプは、プログラム内で Thread.getAllStackTraces メソッドを使って、またはデバッガ内ですべてのスレッドスタックを出力するデバッガオプション (jdb サンプルデバッガの場合は where コマンド) を使って取得することもできます。
jstack ユーティリティーの詳細については、マニュアルページを参照してください。
jstack pid コマンドがハングアッププロセスのために応答しない場合、次の例のように -F オプション (Solaris OS および Linux の場合のみ) を使ってスタックダンプを強制できます。
$ jstack -F 8321 Attaching to process ID 8321, please wait... Debugger attached successfully. Client compiler detected. JVM version is 1.6.0-rc-b100 Deadlock Detection: Found one Java-level deadlock: ============================= "Thread2": waiting to lock Monitor@0x000af398 (Object@0xf819aa10, a java/lang/String), which is held by "Thread1" "Thread1": waiting to lock Monitor@0x000af400 (Object@0xf819aa48, a java/lang/String), which is held by "Thread2" Found a total of 1 deadlock. Thread t@2: (state = BLOCKED) Thread t@11: (state = BLOCKED) - Deadlock$DeadlockMakerThread.run() @bci=108, line=32 (Interpreted frame) Thread t@10: (state = BLOCKED) - Deadlock$DeadlockMakerThread.run() @bci=108, line=32 (Interpreted frame) Thread t@6: (state = BLOCKED) Thread t@5: (state = BLOCKED) - java.lang.Object.wait(long) @bci=-1107318896 (Interpreted frame) - java.lang.Object.wait(long) @bci=0 (Interpreted frame) - java.lang.ref.ReferenceQueue.remove(long) @bci=44, line=116 (Interpreted frame) - java.lang.ref.ReferenceQueue.remove() @bci=2, line=132 (Interpreted frame) - java.lang.ref.Finalizer$FinalizerThread.run() @bci=3, line=159 (Interpreted frame) Thread t@4: (state = BLOCKED) - java.lang.Object.wait(long) @bci=0 (Interpreted frame) - java.lang.Object.wait(long) @bci=0 (Interpreted frame) - java.lang.Object.wait() @bci=2, line=485 (Interpreted frame) - java.lang.ref.Reference$ReferenceHandler.run() @bci=46, line=116 (Interpreted frame)
コアダンプからスタックトレースを取得するには、次のコマンドを実行します。
$ jstack $JAVA_HOME/bin/java core
jstack ユーティリティーは、混合スタックの出力に使用することもできます (つまり、Java スタックのほかにネイティブスタックフレームも出力できます)。ネイティブフレームとは、VM コードや JNI/ネイティブコードに関連付けられた C/C++ フレームのことです。
混合スタックを出力するには、次の例のように -m オプションを使用します。
$ jstack -m 21177 Attaching to process ID 21177, please wait... Debugger attached successfully. Client compiler detected. JVM version is 1.6.0-rc-b100 Deadlock Detection: Found one Java-level deadlock: ============================= "Thread1": waiting to lock Monitor@0x0005c750 (Object@0xd4405938, a java/lang/String), which is held by "Thread2" "Thread2": waiting to lock Monitor@0x0005c6e8 (Object@0xd4405900, a java/lang/String), which is held by "Thread1" Found a total of 1 deadlock. ----------------- t@1 ----------------- 0xff2c0fbc __lwp_wait + 0x4 0xff2bc9bc _thrp_join + 0x34 0xff2bcb28 thr_join + 0x10 0x00018a04 ContinueInNewThread + 0x30 0x00012480 main + 0xeb0 0x000111a0 _start + 0x108 ----------------- t@2 ----------------- 0xff2c1070 ___lwp_cond_wait + 0x4 0xfec03638 bool Monitor::wait(bool,long) + 0x420 0xfec9e2c8 bool Threads::destroy_vm() + 0xa4 0xfe93ad5c jni_DestroyJavaVM + 0x1bc 0x00013ac0 JavaMain + 0x1600 0xff2bfd9c _lwp_start ----------------- t@3 ----------------- 0xff2c1070 ___lwp_cond_wait + 0x4 0xff2ac104 _lwp_cond_timedwait + 0x1c 0xfec034f4 bool Monitor::wait(bool,long) + 0x2dc 0xfece60bc void VMThread::loop() + 0x1b8 0xfe8b66a4 void VMThread::run() + 0x98 0xfec139f4 java_start + 0x118 0xff2bfd9c _lwp_start ----------------- t@4 ----------------- 0xff2c1070 ___lwp_cond_wait + 0x4 0xfec195e8 void os::PlatformEvent::park() + 0xf0 0xfec88464 void ObjectMonitor::wait(long long,bool,Thread*) + 0x548 0xfe8cb974 void ObjectSynchronizer::wait(Handle,long long,Thread*) + 0x148 0xfe8cb508 JVM_MonitorWait + 0x29c 0xfc40e548 * java.lang.Object.wait(long) bci:0 (Interpreted frame) 0xfc40e4f4 * java.lang.Object.wait(long) bci:0 (Interpreted frame) 0xfc405a10 * java.lang.Object.wait() bci:2 line:485 (Interpreted frame) ... more lines removed here to reduce output... ----------------- t@12 ----------------- 0xff2bfe3c __lwp_park + 0x10 0xfe9925e4 AttachOperation*AttachListener::dequeue() + 0x148 0xfe99115c void attach_listener_thread_entry(JavaThread*,Thread*) + 0x1fc 0xfec99ad8 void JavaThread::thread_main_inner() + 0x48 0xfec139f4 java_start + 0x118 0xff2bfd9c _lwp_start ----------------- t@13 ----------------- 0xff2c1500 _door_return + 0xc ----------------- t@14 ----------------- 0xff2c1500 _door_return + 0xc
先頭に「*」が付いているフレームが Java フレーム、先頭に「*」が付いていないフレームがネイティブ C/C++ フレームです。
このユーティリティーの出力をパイプ経由で c++filt に渡せば、C++ の分解されたシンボル名を分解解除できます。HotSpot 仮想マシンは C++ 言語で開発されているので、jstack ユーティリティーは HotSpot 内部関数に対して C++ 分解シンボル名を出力します。c++filt ユーティリティーは、次のネイティブ c++ コンパイラスイートとともに配布されています。SUNWspro (Solaris OS) および gnu (Linux)。
jstat ユーティリティーは、HotSpot VM の組み込みインストゥルメンテーションを使って、実行中アプリケーションのパフォーマンスやリソース消費に関する情報を提供します。このツールは、パフォーマンスの問題、特にヒープサイズ決定やガベージコレクションに関係する問題を診断するときに使用できます。jstat ユーティリティーでは、特殊なオプション付きで VM を起動する必要はありません。HotSpot VM の組み込みインストゥルメンテーションはデフォルトで有効になります。このユーティリティーは、Sun がサポートするすべてのオペレーティングシステムプラットフォームの JDK ダウンロードに含まれています。
注 - Windows 98 や Windows ME ではインストゥルメンテーションにアクセスできません。さらに、Windows NT、2000、または XP で FAT32 ファイルシステムが使用されている場合、インストゥルメンテーションにアクセスできません。
次のリストは、jstat ユーティリティーのオプションを示しています。
class - クラスローダーの動作に関する統計を出力します。
compiler - HotSpot コンパイラの動作の統計を出力します。
gc - ガベージコレクトされるヒープの動作の統計を出力します。
gccapacity - 世代と対応する領域の容量に関する統計を出力します。
gccause - ガベージコレクション統計のサマリー (-gcutil と同じ) と、直前および現在 (適用可能な場合) のガベージコレクションイベントの原因を出力します。
gcnew - New 世代の動作の統計を出力します。
gcnewcapacity - New 世代と対応する領域のサイズに関する統計を出力します。
gcold - Old 世代および Permanent 世代の動作に関する統計を出力します。
gcoldcapacity - Old 世代のサイズに関する統計を出力します。
gcpermcapacity - Permanent 世代のサイズに関する統計を出力します。
gcutil - ガベージコレクション統計のサマリーを出力します。
printcompilation - HotSpot コンパイル方法の統計を出力します。
jstat ユーティリティーの完全な説明については、マニュアルページを参照してください。
ドキュメントにはいくつか例が含まれていて、それらのいくつかをこのドキュメントのここでそのまま掲載します。
jstat ユーティリティーは、vmid を使ってターゲットプロセスを識別します。vmid の構文はドキュメントに記載されていますが、もっとも単純な場合、vmid はローカル仮想マシン識別子になることができます。Solaris OS、Linux、および Windows の場合、これはプロセス ID とみなすことができます。これが標準的ですが、常にそうであるとはかぎりません。
jstat ツールで提供されるデータは、Solaris OS および Linux のツール vmstat および iostat で提供されるデータに似ています。
データをグラフィカルに表現する場合は、visualgc ツールを使用できます。「2.14 visualgc ツール」を参照してください。
次は -gcutil オプションの例です。ユーティリティーは、lvmid 2834 に接続し、250 ミリ秒間隔で 9 つのサンプルを取り、出力を表示しています。
$ jstat -gcutil 2834 250 9 S0 S1 E O P YGC YGCT FGC FGCT GCT 0.00 0.00 87.14 46.56 96.82 54 1.197 140 86.559 87.757 0.00 0.00 91.90 46.56 96.82 54 1.197 140 86.559 87.757 0.00 0.00 100.00 46.56 96.82 54 1.197 140 86.559 87.757 0.00 27.12 5.01 54.60 96.82 55 1.215 140 86.559 87.774 0.00 27.12 11.22 54.60 96.82 55 1.215 140 86.559 87.774 0.00 27.12 13.57 54.60 96.82 55 1.215 140 86.559 87.774 0.00 27.12 18.05 54.60 96.82 55 1.215 140 86.559 87.774 0.00 27.12 23.85 54.60 96.82 55 1.215 140 86.559 87.774 0.00 27.12 27.32 54.60 96.82 55 1.215 140 86.559 87.774
この例の出力は、若い世代のコレクションが 3 番目と 4 番目のサンプル間で行われたことを示しています。コレクションには 0.017 秒かかっており、オブジェクトが eden 領域 (E) から Old 領域 (O) に昇格したため、old 領域の使用率は 46.56% から 54.60% に増加しています。
次の例は、-gcnew オプションを示しています。ユーティリティーは、lvmid 2834 に接続し、250 ミリ秒間隔でサンプルを取り、出力を表示しています。さらに、-h3 オプションを使用して、データが 3 行表示されるごとに列ヘッダーを表示します。
$ jstat -gcnew -h3 2834 250 S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT 192.0 192.0 0.0 0.0 15 15 96.0 1984.0 942.0 218 1.999 192.0 192.0 0.0 0.0 15 15 96.0 1984.0 1024.8 218 1.999 192.0 192.0 0.0 0.0 15 15 96.0 1984.0 1068.1 218 1.999 S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT 192.0 192.0 0.0 0.0 15 15 96.0 1984.0 1109.0 218 1.999 192.0 192.0 0.0 103.2 1 15 96.0 1984.0 0.0 219 2.019 192.0 192.0 0.0 103.2 1 15 96.0 1984.0 71.6 219 2.019 S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT 192.0 192.0 0.0 103.2 1 15 96.0 1984.0 73.7 219 2.019 192.0 192.0 0.0 103.2 1 15 96.0 1984.0 78.0 219 2.019 192.0 192.0 0.0 103.2 1 15 96.0 1984.0 116.1 219 2.019
この例では、ヘッダー文字列の繰り返しが見られるほかにも、4 番目と 5 番目のサンプル間で若い世代のコレクションが発生し、その継続時間が 0.02 秒だったことがわかります。このコレクションでは、Survivor 領域 0 の使用率 (S1U) が適切な Survivor サイズ (DSS) を超過するのに十分なライブデータが検出されました。この結果、オブジェクトは、Old 世代 (この出力には非表示) へ昇格され、殿堂入りしきい値 (TT) が、15 から 1 へ降格されました。
次の例は、-gcoldcapacity オプションを示しています。ユーティリティーは、lvmid 21891 へ接続し、250 ミリ秒間隔で 3 つのサンプルを取得しています。-t オプションを使用して、最初の列にサンプルごとのタイムスタンプを表示しています。
$ jstat -gcoldcapacity -t 21891 250 3 Timestamp OGCMN OGCMX OGC OC YGC FGC FGCT GCT 150.1 1408.0 60544.0 11696.0 11696.0 194 80 2.874 3.799 150.4 1408.0 60544.0 13820.0 13820.0 194 81 2.938 3.863 150.7 1408.0 60544.0 13820.0 13820.0 194 81 2.938 3.863
Timestamp 列には、ターゲット Java VM の起動時からの経過時間が、秒単位で報告されています。さらに、-gcoldcapacity 出力では、割り当て要求または昇格要求あるいはその両方を満たすためにヒープが拡張するごとに、Old 世代の容量 (OGC) と Old 領域の容量 (OC) とが増加していることがわかります。Old 世代の容量 (OGC) は、81 番目のフル GC (FGC) 後に、11696 K バイトから 13820 K バイトへ増加しています。Old 世代 (および領域) の最大容量は、60544 K バイト (OGCMX) なので、まだ拡張できる余裕が残されています。
jstatd デーモンは、計測された Java HotSpot 仮想マシンの作成と終了をモニターし、ローカルシステム上で実行している Java VM にリモートモニタリングツールが接続することを許可するインタフェースを提供する、Remote Method Invocation (RMI) サーバーアプリケーションです。たとえば、このデーモンを使えば、jps ユーティリティーでリモートシステム上のプロセスを一覧表示できます。
注 - Windows 98 や Windows ME ではインストゥルメンテーションにアクセスできません。さらに、Windows NT、2000、または XP で FAT32 ファイルシステムが使用されている場合、インストゥルメンテーションにアクセスできません。
詳細な使用例など、jstatd デーモンの詳細については、マニュアルページを参照してください。
visualgc ツールは jstat ツールに関係しています。(「2.12 jstat ユーティリティー」を参照。)visualgc ツールは、ガベージコレクション (GC) システムのグラフィカル表示を提供します。これは jstat と同様に、HotSpot VM の組み込みインストゥルメンテーションを使用します。
visualgc ツールは JDK リリースには含まれていませんが、jvmstat 3.0 サイトから個別のダウンロードとして入手できます。
次の画面出力は、GC やヒープがどのように視覚化されるかを示しています。
Solaris OS または Linux の場合、アプリケーションコンソール (標準入力) で Ctrl キーとバックスラッシュ (\) キーの組み合わせを押すと、HotSpot VM からアプリケーションの標準出力にスレッドダンプが出力されます。Windows の場合、同等のキーシーケンスは Ctrl キーと Break キーです。これらのキーの組み合わせに対する一般的な用語は、Ctrl-Break ハンドラです。
Solaris OS および Linux の場合、Java プロセスが QUIT シグナルを受信すると、スレッドダンプが出力されます。したがって、kill -QUIT pid コマンドを実行すると、pid を ID に持つプロセスから標準出力にスレッドダンプが出力されます。
次の各サブセクションでは、Ctrl-Break ハンドラの出力について詳しく説明します。
スレッドダンプは、仮想マシン内のすべての Java スレッドに対するスレッドスタック (スレッド状態を含む) から構成されます。スレッドダンプが出力されてもアプリケーションは終了しません。スレッド情報の出力後も継続されます。
次の例はスレッドダンプを示しています。
Full thread dump Java HotSpot(TM) Client VM (1.6.0-rc-b100 mixed mode): "DestroyJavaVM" prio=10 tid=0x00030400 nid=0x2 waiting on condition [0x00000000..0xfe77fbf0] java.lang.Thread.State: RUNNABLE "Thread2" prio=10 tid=0x000d7c00 nid=0xb waiting for monitor entry [0xf36ff000..0xf36ff8c0] java.lang.Thread.State: BLOCKED (on object monitor) at Deadlock$DeadlockMakerThread.run(Deadlock.java:32) - waiting to lock <0xf819a938> (a java.lang.String) - locked <0xf819a970> (a java.lang.String) "Thread1" prio=10 tid=0x000d6c00 nid=0xa waiting for monitor entry [0xf37ff000..0xf37ffbc0] java.lang.Thread.State: BLOCKED (on object monitor) at Deadlock$DeadlockMakerThread.run(Deadlock.java:32) - waiting to lock <0xf819a970> (a java.lang.String) - locked <0xf819a938> (a java.lang.String) "Low Memory Detector" daemon prio=10 tid=0x000c7800 nid=0x8 runnable [0x00000000..0x00000000] java.lang.Thread.State: RUNNABLE "CompilerThread0" daemon prio=10 tid=0x000c5400 nid=0x7 waiting on condition [0x00000000..0x00000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" daemon prio=10 tid=0x000c4400 nid=0x6 waiting on condition [0x00000000..0x00000000] java.lang.Thread.State: RUNNABLE "Finalizer" daemon prio=10 tid=0x000b2800 nid=0x5 in Object.wait() [0xf3f7f000..0xf3f7f9c0] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0xf4000b40> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) - locked <0xf4000b40> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159) "Reference Handler" daemon prio=10 tid=0x000ae000 nid=0x4 in Object.wait() [0xfe57f000..0xfe57f940] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0xf4000a40> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:485) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) - locked <0xf4000a40> (a java.lang.ref.Reference$Lock) "VM Thread" prio=10 tid=0x000ab000 nid=0x3 runnable "VM Periodic Task Thread" prio=10 tid=0x000c8c00 nid=0x9 waiting on condition
出力は、各スレッドのヘッダーとスタックトレースから構成されます。各スレッドは空行で区切られます。Java スレッド (Java 言語コードを実行できるスレッド) がまず出力され、それらのあとに VM 内部スレッドに関する情報が続きます。
ヘッダー行には、スレッドに関する次の情報が含まれます。
スレッド名
スレッドがデーモンスレッドかどうかのマーク
スレッド優先順位 (prio)
スレッド ID (tid、メモリー内のスレッド構造体のアドレス)
ネイティブスレッドの ID (nid)
スレッド状態 (スレッドダンプの時点でスレッドが何を実行していたかを示す)
アドレス範囲 (スレッドのおおよその有効なスタック領域を表す)
次の表に、出力される可能性のあるスレッド状態の一覧を示します。
|
スレッドヘッダーのあとにスレッドスタックが続きます。
Ctrl-Break ハンドラは、スレッドスタックだけでなく、デッドロック検出アルゴリズムを実行します。デッドロックが検出された場合、デッドロックされた各スレッドに関する追加情報がスレッドダンプのあとに出力されます。
Found one Java-level deadlock: ============================= "Thread2": waiting to lock monitor 0x000af330 (object 0xf819a938, a java.lang.String), which is held by "Thread1" "Thread1": waiting to lock monitor 0x000af398 (object 0xf819a970, a java.lang.String), which is held by "Thread2" Java stack information for the threads listed above: =================================================== "Thread2": at Deadlock$DeadlockMakerThread.run(Deadlock.java:32) - waiting to lock <0xf819a938> (a java.lang.String) - locked <0xf819a970> (a java.lang.String) "Thread1": at Deadlock$DeadlockMakerThread.run(Deadlock.java:32) - waiting to lock <0xf819a970> (a java.lang.String) - locked <0xf819a938> (a java.lang.String) Found 1 deadlock.
Java VM フラグ -XX:+PrintConcurrentLocks が設定されている場合、Ctrl-Break は各スレッドが所有している並行ロックのリストも出力します。
JDK 7 以降、Ctrl-Break ハンドラはヒープサマリーも出力します。この出力では、さまざまな世代 (ヒープの領域) がそのサイズ、使用量、アドレス範囲とともに表示されます。アドレス範囲は特に、pmap などのツールでプロセスを調査する場合にも役立ちます。
Heap def new generation total 1152K, used 435K [0x22960000, 0x22a90000, 0x22e40000 ) eden space 1088K, 40% used [0x22960000, 0x229ccd40, 0x22a70000) from space 64K, 0% used [0x22a70000, 0x22a70000, 0x22a80000) to space 64K, 0% used [0x22a80000, 0x22a80000, 0x22a90000) tenured generation total 13728K, used 6971K [0x22e40000, 0x23ba8000, 0x269600 00) the space 13728K, 50% used [0x22e40000, 0x2350ecb0, 0x2350ee00, 0x23ba8000) compacting perm gen total 12288K, used 1417K [0x26960000, 0x27560000, 0x2a9600 00) the space 12288K, 11% used [0x26960000, 0x26ac24f8, 0x26ac2600, 0x27560000) ro space 8192K, 62% used [0x2a960000, 0x2ae5ba98, 0x2ae5bc00, 0x2b160000) rw space 12288K, 52% used [0x2b160000, 0x2b79e410, 0x2b79e600, 0x2bd60000)
Java VM フラグ -XX:+PrintClassHistogram が設定されている場合、Ctrl-Break ハンドラはヒープヒストグラムを生成します。
このセクションでは、トラブルシューティングやモニタリング目的に役立ついくつかのオペレーティングシステム固有ツールの一覧を示します。各ツールについて簡単な説明が提供されます。詳細については、オペレーティングシステムのドキュメント (Solaris OS や Linux の場合はマニュアルページ) を参照してください。
次のツールは、Solaris オペレーティングシステムで提供されます。「2.16.4 Solaris 10 OS で導入されたツール」も参照してください (バージョン 10 の Solaris OS で導入されたいくつかのツールの詳細が提供されます)。
|
次のツールは、Linux オペレーティングシステムで提供されます。
|
次のツールは、Windows オペレーティングシステムで提供されます。さらに、MSDN ライブラリサイトにアクセスし、デバッグサポートを検索できます。
|
このセクションでは、バージョン 10 の Solaris オペレーティングシステムで導入された診断ツールのいくつかについて詳しく説明します。
pmap ユーティリティーは、Solaris 10 OS でスタックセグメントがテキスト [stack] 付きで出力されるように改善されました。このテキストはスタックの場所を見つけるのに役立ちます。
次の例は、このツールからの出力の一部を示しています。
19846: /net/myserver/export1/user/j2sdk6/bin/java -Djava.endorsed.d 00010000 72K r-x-- /export/disk09/jdk/6/rc/b63/binaries/solsparc/bin/java 00030000 16K rwx-- /export/disk09/jdk/6/rc/b63/binaries/solsparc/bin/java 00034000 32544K rwx-- [ heap ] D1378000 32K rwx-R [ stack tid=44 ] D1478000 32K rwx-R [ stack tid=43 ] D1578000 32K rwx-R [ stack tid=42 ] D1678000 32K rwx-R [ stack tid=41 ] D1778000 32K rwx-R [ stack tid=40 ] D1878000 32K rwx-R [ stack tid=39 ] D1974000 48K rwx-R [ stack tid=38 ] D1A78000 32K rwx-R [ stack tid=37 ] D1B78000 32K rwx-R [ stack tid=36 ] [.. more lines removed here to reduce output ..] FF370000 8K r-x-- /usr/lib/libsched.so.1 FF380000 8K r-x-- /platform/sun4u-us3/lib/libc_psr.so.1 FF390000 16K r-x-- /lib/libthread.so.1 FF3A4000 8K rwx-- /lib/libthread.so.1 FF3B0000 8K r-x-- /lib/libdl.so.1 FF3C0000 168K r-x-- /lib/ld.so.1 FF3F8000 8K rwx-- /lib/ld.so.1 FF3FA000 8K rwx-- /lib/ld.so.1 FFB80000 24K ----- [ anon ] FFBF0000 64K rwx-- [ stack ] total 167224K
Solaris 10 OS リリースより前の pstack ユーティリティーは、Java 言語をサポートしていませんでした。解釈済みおよび (HotSpot で) コンパイル済み Java メソッドの両方で、16 進アドレスを出力していました。
Solaris 10 OS 以降の pstack コマンド行ツールは、コアファイルまたはライブプロセスから混合モードのスタックトレース (Java および C/C++ フレーム) を出力します。このツールは、解釈済み、コンパイル済み、およびインライン化された Java メソッドに対し、Java メソッド名を出力します。
Solaris 10 OS に含まれる DTrace ツールを使えば、オペレーティングシステムのカーネルおよびユーザーレベルのプログラムの動的トレースを行えます。このツールでは、システムコールの入口と出口、ユーザーモード関数の入口と出口、およびその他多くのプローブポイントでのスクリプトがサポートされます。スクリプトは、安全なポインタセマンティクスを備えた、C に似た言語である D プログラミング言語で記述されます。これらのスクリプトは、問題のトラブルシューティングやパフォーマンス問題の解決に役立つ可能性があります。
dtrace コマンドは、DTrace ツールに対する汎用のフロントエンドです。このコマンドには、D 言語を呼び出したり、バッファー内のトレースデータを取得したり、トレースデータを書式設定/出力するための一連の基本ルーチンにアクセスしたりするための、単純なインタフェースが用意されています。
カスタマイズされた独自の DTrace スクリプトを D 言語を使って記述したり、すでにさまざまなサイト上で入手可能な多くのスクリプトから 1 つ以上をダウンロードして使用したりすることもできます。
プローブは、プロバイダと呼ばれるカーネルモジュールによって提供および計測されます。プローブプロバイダによって提供されるタイプのトレースには、ユーザー命令トレース、関数境界トレース、カーネルロック計測、プロファイル割り込み、システムコールトレースなどがあります。独自のスクリプトを記述する場合は、D 言語を使ってプローブを有効にしてくださう。この言語では条件トレースや出力書式設定を行うこともできます。
dtrace -l オプションを使って、Solaris OS で使用可能な一連のプロバイダやプローブを調べることができます。
DTrace Toolkit は、OpenSolaris DTrace コミュニティーで開発されたドキュメント付きの有用なスクリプトを集めたものです。http://www.opensolaris.org/os/community/dtrace/dtracetoolkit/ を参照してください
DTrace の詳細情報が次の場所で提供されています。
Solaris 動的トレースガイド:http://docs.sun.com/app/docs/doc/817-6223/
BigAdmin システム管理ポータル (DTrace 用):http://www.sun.com/bigadmin/content/dtrace/
JDK 7 以降では、Java HotSpot VM に組み込みプローブプロバイダが 2 つ含まれています。hotspot と hotspot_jni です。これらのプロバイダが提供するプローブを使えば、VM および実行中の Java アプリケーションの内部状態やアクティビティーをモニターできます。
hotspot プロバイダのプローブは次のように分類できます。
VM のライフサイクル: VM の初期化の開始と終了、および VM のシャットダウン。
スレッドのライフサイクル: スレッドの開始と停止、スレッド名、スレッド ID など。
クラスのロード: Java クラスのロードとアンロード。
ガベージコレクション: ガベージコレクションの開始と停止、システム全体またはメモリープール別。
メソッドのコンパイル: メソッドのコンパイルの開始と終了、メソッドのロードとアンロード。
モニタープローブ: 待機イベント、通知イベント、競合するモニターへの出入り。
アプリケーションの追跡: メソッドに入る、メソッドから戻る、Java オブジェクトの割り当て。
ネイティブコードから Java コードへの呼び出しの場合、ネイティブコードは JNI インタフェース経由で呼び出しを行う必要があります。hotspot_jni プロバイダは、Java コードを呼び出したり VM の状態を検査したりするために JNI インタフェースに用意されているメソッドごとに、入口と出口における DTrace プローブを管理します。
プローブポイントで現在のスレッドのスタックトレースを出力するには、ustack 組み込み関数を使用します。この関数は、C/C++ ネイティブ関数の名前だけでなく、Java メソッドの名前も出力します。次は、あるスレッドが read システムコールを呼び出したときに完全なスタックトレースを出力する単純な D スクリプトです。
#!/usr/sbin/dtrace -s syscall::read:entry /pid == $1 && tid == 1/ { ustack(50, 0x2000); }
上のスクリプトは read.d という名前のファイルに格納され、次のコマンドで実行されます。
read.d pid-of-the-Java-process-that-is-traced
Java アプリケーションで多数の I/O が生成されたり、なんらかの予期しない遅延が発生したりした場合、DTrace ツールやその ustack() アクションを使用すれば、問題を診断しやすくなる可能性があります。
JDK ソフトウェアに含まれる広範なアプリケーションプログラミングインタフェース (API) を使えば、Java 実行環境に配備されたアプリケーションの問題を監視、モニター、プロファイル、デバッグ、および診断するためのツールを開発できます。新しいツールの開発はこのドキュメントの対象外です。代わりにこのセクションでは、使用可能なプログラミングインタフェースの概要を簡単に説明します。JDK ダウンロードに含まれるコード例やデモコードも参照してください。
java.lang.management パッケージは、Java 仮想マシンやオペレーティングシステムをモニターおよび管理するための管理インタフェースを提供します。具体的には、次の各システムのインタフェースがカバーされています。
クラスのロード
コンパイル
ガベージコレクション
メモリーマネージャー
ランタイム
スレッド
java.lang.management パッケージは、Java SE API ドキュメントで詳しく説明されています。
JDK リリースには、java.lang.management パッケージの使用方法を示すコード例が含まれています。これらの例は $JAVA_HOME/demo/management ディレクトリで見つけることができます。これらの例のいくつかを次に示します。
MemoryMonitor - java.lang.management API を使ってアプリケーションが消費するすべてのメモリープールのメモリー使用量を監視する方法を示します。
FullThreadDump - java.lang.management API を使って完全なスレッドダンプを取得し、プログラムでデッドロックを検出する方法を示します。
VerboseGC - java.lang.management API を使ってアプリケーションのガベージコレクション統計やメモリー使用量を出力する方法を示します。
JDK リリースには java.lang.management パッケージのほかに、プラットフォーム拡張が com.sun.management パッケージとして含まれています。プラットフォーム拡張には、コレクションをサイクルで実行するガベージコレクタから詳細統計を取得するための管理インタフェースが含まれています。これらの拡張には、オペレーティングシステムから追加のメモリー統計を取得するための管理インタフェースも含まれています。
プラットフォーム拡張の詳細については、Java SE API ドキュメント: Java プラットフォーム用の監視および管理インタフェースで見つけることができます。
java.lang.instrument パッケージは、Java プログラミング言語エージェントが Java VM で実行中のプログラムを計測することを許可するサービスを提供します。インストゥルメンテーションは、プロファイラなどのツールや、メソッド呼び出しをトレースするためのツールなどで使用されます。このパッケージを使えば、ロード時および動的計測の両方が容易になります。また、ロード済みクラスに関する情報や特定のオブジェクトが消費するストレージ量に関する情報を取得するメソッドも含まれています。
java.lang.instrument パッケージは、Java SE API ドキュメントで詳しく説明されています。
java.lang.Thread クラスには、すべてのライブスレッドのスタックトレースのマップを返す、getAllStackTraces という名前の静的メソッドが含まれています。Thread クラスにはさらに、スレッドの状態を返す getState という名前のメソッドも含まれています。状態は java.lang.Thread.State 列挙で定義されます。これらのメソッドは、診断またはモニタリング機能をアプリケーションに追加する際に役立つ可能性があります。これらのメソッドは、API ドキュメントで詳しく説明されています。
Java Virtual Machine Tools Interface (JVM TI) は、広範な開発/モニタリングツールの開発に使用可能な、ネイティブ (C/C++) プログラミングインタフェースです。JVM TI は、VM の状態にアクセスする必要のある各種ツール (プロファイリングツール、デバッグツール、モニタリングツール、スレッド分析ツール、カバレージ分析ツールを含むがこれに限らない) のためのインタフェースを提供します。
JVM TI に依存するエージェントのいくつかの例を次に示します。
HPROF プロファイラ (「2.1 HPROF - ヒーププロファイラ」を参照)
Java Debug Wire Protocol (JDWP) エージェント (「2.17.5 Java Platform Debugger Architecture」を参照)
java.lang.instrument 実装 (「2.17.2 java.lang.instrument パッケージ」を参照)
JVM TI の仕様は、JVM Tool Interface のドキュメントで見つけることができます。
JDK リリースには、JVM TI の使用方法を示すコード例が含まれています。これらの例は $JAVA_HOME/demo/jvmti ディレクトリで見つけることができます。いくつかの例を次に示します。
mtrace - メソッドの呼び出しおよびリターン回数を追跡するエージェントライブラリ。これは、バイトコードインストゥルメンテーションを使って仮想マシンにロードされたすべてのクラスを計測し、使用頻度の高いメソッドのソート済みリストを出力します。
heapTracker - オブジェクトの割り当てを追跡するエージェントライブラリ。これは、バイトコードインストゥルメンテーションを使ってコンストラクタメソッドを計測します。
heapViewer - Ctrl-\ または Ctrl-Break が押されたときにヒープ統計を出力するエージェントライブラリ。ロードされたクラスごとに、そのクラスのインスタンス数と使用済み容量を出力します。
Java Platform Debugger Architecture (JPDA) は、デバッガやデバッガに似たツールが使用するために設計されたアーキテクチャーです。これは、2 つのプログラミングインタフェースと 1 つのワイヤープロトコルから構成されています。
Java Virtual Machine Tools Interface (JVM TI) は、仮想マシンへのインタフェースです。(「2.17.4 Java Virtual Machine Tools Interface」を参照)。
Java Debug Interface (JDI) は、ユーザーコードレベルでの情報および要求を定義します。これは、Java プログラミング言語アプリケーションをデバッグするための Pure Java プログラミング言語インタフェースです。JPDA での JDI は、デバッガプロセス内での、デバッギプロセスの仮想マシンのリモートビューです。これがフロントエンドによって実装され、デバッガライクなアプリケーション (IDE、デバッガ、トレーサ、モニタリングツールなど) がクライアントです。
Java Debug Wire Protocol (JDWP) は、デバッグされるプロセスと、JDI を実装したデバッガフロントエンドとの間で転送される、情報および要求の形式を定義します。
JPDA の完全な説明 (仕様を含む) は、Java Platform Debugger Architecture (JPDA) のドキュメントにあります。
JPDA 構造のグラフィック表示は、Java Platform Debugger Architecture の説明で示されています。
jdb ユーティリティーは、コマンド行デバッガの例として JDK リリースに含まれています。jdb ユーティリティーは、Java Debug Interface (JDI) を使ってターゲット VM を起動または接続します。「2.4 jdb ユーティリティー」を参照してください。
JDI を使えば、従来のデバッガタイプのツールだけでなく、ポストモーテム診断の際や、ツールが非協力的なプロセス (ハングアッププロセスなど) に接続する必要があるシナリオで役に立つツールを開発することもできます。JDI ベースのツールをクラッシュダンプやハングアッププロセスに接続するために使用可能な JDI コネクタについては、「2.4 jdb ユーティリティー」を参照してください。