![]() ![]() ![]() ![]() |
この章では、Oracle JRockit Mission Control を使用して Oracle JRockit JVM 上で実行されるアプリケーションをモニタおよび管理できる、さまざまな方法を例示しています。以下の使用事例を説明します。
Marcus は、JRockit JVM インスタンス上で実行している DemoLeak というアプリケーションをモニタして、最大限のパフォーマンスが得られるようにチューニングされているかどうかを確認したいと思っています。それを行うには、アプリケーションの実行と同時に、JRockit Management Console を実行します。Management Console は、メモリ、CPU 使用状況、その他の実行時メトリックに関する情報をリアルタイムに提供します。Management Console はマルチタブ インタフェースで、各タブによって、実行中のアプリケーションのモニタや、ある局面の管理を行うことができます。お使いのバージョンの Management Console で使用されるタブは、コンソールと一緒にインストールした Java プラグインによって変わります。完全に実装すると、コンソールには 8 つのタブと 1 つのメニューが表示され、7 つのプラグインにマップされます。
まずマルクスは、次のように入力してコマンド プロンプトから JRockit Mission Control Client を起動します。
jrockit\bin\jrmc
JRockit Mission Control Client が起動している間に、DemoLeak アプリケーションを起動します。コマンド プロンプトには、次のように入力します。
jrockit\bin\java DemoLeak
次に、ローカル接続で Management Console を起動します。
Management Console を起動するには、次の手順を実行します。
アプリケーション パフォーマンス上の問題を発見する方法の 1 つは、実行時のメモリの使用状況を確認することです。使用可能なメモリをアプリケーションがどのように使っているかを分析するには、[メモリ] タブを使用します。このタブでは、ヒープ使用状況、メモリ使用状況、およびガベージ コレクション方式に焦点を当てています。このタブに表示される情報は、最大限のアプリケーション パフォーマンスを引き出すように JRockit JVM をコンフィグレーションできているかどうかの判断に、非常に役立ちます。
次に、ガベージ コレクションごとの期間を確認することにします。よくあることですが、ガベージ コレクションの実行時間が長すぎると、アプリケーションの パフォーマンスが低下します。ガベージ コレクションの期間を確認するために、この情報を [ヒープ] グラフ上でプロットすることができます。
各種タブに表示されるグラフはすべて、いくつかの有用なデフォルト属性を使用してあらかじめコンフィグレーションされていますが、任意の MBean からの任意の数値属性を追加可能です。J2SE 5.0 の標準的な MBean と JRockit JVM 固有の MBean に加えて、JRockit Mission Control 自体にも、他の複数の属性から属性を派生させる、いわゆる合成 MBean が用意されています。そのような属性の 1 つが、ガベージ コレクションの実行時間です。
注意 : | ガベージ コレクション期間の属性は PauseTimes という名前ですが、Java アプリケーションは、ガベージ コレクションの全期間を通して停止し続ける必要はありません。コンカレント ガベージ コレクタを使用している場合、ガベージ コレクタは、ガベージ コレクションの期間のほとんどの間、Java アプリケーションと並行して処理を行います。この属性の名前が誤解を招きやすいことは確認済みの問題であり、将来のリリースで修正される予定です。修正後の属性名は Duration となる予定です。 |
システムのボトルネックを検出しようとして CPU の負荷のグラフを調べているときに、JVM の CPU 負荷が最大限に達していることに気付きました。より長い期間にわたって調べた場合、この現象がどの程度の頻度で発生するかを確認したいとします。CPU グラフを長時間観察し続ける代わりに、VM によって生成される CPU の負荷が高くなったときに警告トリガが通知されるように設定します。
CPU の負荷の値の範囲は 0 ~ -1 であるため、[最大トリガ値] に 0.95 を指定します。5 分間隔で CPU の使用状況が最大に達したときに警告を発生させるため、[持続時間 [秒]] に 300 を指定します。また、10 秒未満の間隔でのトリガ発生を回避するため、[限界時間 [秒]] を 10 に設定します。
CPU load for JRockit > 0.95
)、[Finish] をクリックします。
次にマルクスは、ある特定のメソッドが実行されている回数と期間を確認することにしました。これは、メソッド プロファイリングと呼ばれるプロセスです。JRockit Mission Control には、メソッドをプロファイリングするための 2 つのツールが用意されています。
[メソッド プロファイラ] タブを使用してメソッドをプロファイリングするには、次の手順を実行します。
[テンプレートの追加] ダイアログ ボックスが表示されます。
ダイアログ ボックスが閉じ、新しいテンプレートがリストに追加されます (図 23-12)。
[メソッド プロファイラへのクラスの追加] ダイアログ ボックスが表示されます。
java.util.Hashtable
と入力します。java.util.Hashtable
クラスを展開し、下にスクロールして put(Object, Object)
メソッドおよび remove(Object)
メソッドの前にあるボックスをチェックします (図 23-15 を参照)。Hashtable.put(Object, Object)
の呼び出し回数の増え方が、Hashtable.remove(Object)
の呼び出し回数よりもわずかに速いことが分かりました。
フィオナは、アプリケーション DemoLeak のパフォーマンスに不満を抱いています。特に、実行時間が長くなった場合のアプリケーションのパフォーマンス状況が気になります。たとえば、実行し始めて間もないときにはうまく機能しているのに、しばらくすると間違った結果を報告したり、不適切なところで例外を送出したりするようになるのです。また何度実行しても、最終的にはほぼ同じ時間でハングアップしてしまうことにも気付きました。何が問題なのかを見極めるため、フィオナは JRockit Runtime Analyzer (JRA) を使用して、実行時分析を作成することにします。
JRA は、オンデマンドの「フライト レコーダ」として、JVM および JVM が実行しているアプリケーションに関する詳細な記録を生成します。記録されたプロファイルは、後から JRA ツールを使ってオフラインで分析することができます。記録されるデータには、メソッドとロックのプロファイリングのほかに、ガベージ コレクションの統計、最適化の判断、レイテンシ分析 (JRockit Mission Control 3.0) などがあります。
次に、ローカル接続から JRA 記録を作成します。それには、次の手順を実行します。
注意 : | 設定した記録時間が短すぎる (たとえば 30 秒未満) 場合は、おそらく有意義な記録となるに足りるサンプル データは取得できません。 |
JRA 記録の進行状況ウィンドウが表示されます。記録が終了すると、JRA 内にその記録がロードされます。この後、JRA 記録を確認します。
次に、フィオナは JRockit Mission Control Client を使用して、JRA 記録を表示します。まず、[全般] タブを開いて、以下の手順を実行します。
その記録ファイルに関する JRA の [全般] タブが開いて、記録内のデータを確認できるようになります。[全般] タブには、JVM、システム、およびアプリケーションに関する情報が表示されます。このタブは表 23-2 で説明するパネルに分かれています。
このタブを見ることで、フィオナは自分が JVM のどのバージョンを実行したのかを確認できます。また、大きいオブジェクトが 1 秒当たり 22.153 MB の割合または「頻度」で割り当てられている一方で、小さいオブジェクトはそれより大幅に高い 1 秒当たり 261.983 MB という頻度で割り当てられていることも分かります。
次に、[メソッド] タブを確認します。[メソッド] タブには、トップ ホット メソッドが記録中の祖先および子孫と共にリストされます。[メソッド] タブは、表 23-3 で説明する以下のパネルに分かれています。
JRockit JVM におけるメソッド サンプリングは、CPU サンプリングに基づいています。図 23-18 に示すように、[トップ ホット メソッド] セクションには、記録中にサンプリングされたすべてのメソッドがリストされ、最も多くサンプリングされたメソッドから順にソートされます。
注意 : | 記録の際にネイティブ サンプリングを有効化していた場合は、jvm.dll#_qBitSetClear のようにポンド記号 (#) が表示されます。この記号により、JVM 自身や、オペレーティング システムの各種ライブラリなど、ネイティブ ライブラリの関数であることが示されます。 |
トップ ホット メソッドのリストから、最もホットなメソッド 3 つは以下のとおりであることが分かります。
この情報がフィオナにとって、懸念の対象となり得る領域を探し始める際の、有効な手掛かりとなります。最もホットなメソッドとは、最も頻繁にサンプリングされるメソッドであることは分かっています。状況によっては、上位のホット メソッドに対するサンプリング数が、下位のメソッドのサンプリング数の成長を抑制することになります。ホット メソッドは、パフォーマンス上の問題、特にメモリ リークに関しては、有効な指標となります。なぜなら、サンプリング量が大きいと、JVM がその特定のメソッドを実行している時間の長さに影響が及ぶからです。
次にフィオナは、実行時のシステム動作とガベージ コレクションのパフォーマンスをよりよく理解するため、[GC] タブ (図 23-19) を調べます。
このタブは、表 23-4 で説明する 6 つのパネルに分かれています。
[ガベージ コレクション] パネル (図 23-20) のデータを検証したフィオナは、ガベージ コレクションによる休止時間を長いものから 3 つ示すと、「95 (856 ms)」、「41 (707 ms)」、および「73 (691 ms)」であることを見て取りました。
このデータが示唆しているのは、アプリケーション上で処理が続行していくうちに、ガベージ コレクションにかかる時間が長くなっていったということです。これで、アプリケーション パフォーマンス劣化の原因と考えられる要素の診断に役立つ証拠が、さらに得られました。ガベージ コレクションの実行時間が、特に実行時の後になるほど増大していくことで、解放するヒープ領域が減っていることが分かります。以上のことからある程度の確信をもって、メモリ リークが発生していると予測できます。
注意 : | この例では、フィオナはかなり早いうちにメモリ リークの発生を悟ります。これが判明するのは、はっきりとそれを指し示す証拠があるからです。大半の場合、メモリ リークが明らかになるのは、もっと後になってからで、おそらく [GC] タブで自明となることはないでしょう。ここで判明しない場合、ユーザは、「メモリ リークの検出」で説明する JRockit Memory Leak Detector の使用によって、さらに的確な結果を得られます。 |
[GC 全般] タブ (図 23-21) を確認することで、ガベージ コレクションのアクティビティがメモリ リークを示している可能性について、より深い理解を得ることができます。
このタブは、ガベージ コレクション情報を一目で見て取ることができる、3 つのパネルに分かれています。各パネルについては、表 23-5 で説明します。
スタック ツリーをユーザ コードまで展開したフィオナは、多くの割り当てが hashtable
タイプからのものであることを見て取ります。これはつまり、このタイプについて、集中的に割り当てが行われているということです。このタイプの割り当てを減らすことにより、メモリ管理システムに対する負荷を削減できる可能性があります。
次にフィオナは、記録開始時に収集されたオブジェクト統計を、記録後に収集されたオブジェクト統計と比較することが役立つだろうと判断しました。記録セッションの開始時と終了時には、Java ヒープを占めるオブジェクト タイプのうち最も一般的なタイプとクラス、すなわち、インスタンス数の合計がメモリ内に占める領域が最も大きいタイプの、スナップショットが取られます。結果は、[オブジェクト] タブに表示されます (図 23-22)。
[オブジェクト] 統計タブは、表 23-6 で説明する各パネルに分かれます。
ここでも、最も劇的な増加が見られるのは hashtable
であり、これがヒープ上で最も多くのメモリ量を消費していることが分かります。これもまた、メモリ リークが発生していることだけでなく、このリークが hashtable
オブジェクト内で生じているということを顕著に示しています。
フィオナは次に、ロックに関係のあるパフォーマンスのボトルネックの手がかりを見つけるために、ロック統計をチェックすることにしました。そこで、アプリケーションと特定の JRockit JVM インスタンスの双方について、この情報を調べるため、[ロック] タブ (図 23-23) を開きます。
ロック プロファイリング タブは、表 23-7 で定義する各パネルに分かれます。
[Java ロック] パネルを見ると、非競合ロックは他のオブジェクトでは比較的少ないのに対して、hashtable
タイプでは 3 億以上も取得されているということが、即座に分かります。この情報が直接、メモリ リークに結び付くわけではありませんが、低パフォーマンスであることを示してはいます。
ロックの大半は非競合であるため、hashmap
のような非同期のデータ構造に切り替え、競合が発生する可能性がある少数のケースにのみ同期を使用することで、アプリケーションを最適化できます。
アプリケーションの実行状況が悪いのは、メモリ リークのせいであると判断したフィオナは、その疑惑を確信に変え、修正措置をとり始めるために、JRockit Memory Leak Detector の Memory Leak Detector を利用できます。メモリ リークは、不要になったメモリをプログラムが解放できずにいる場合に発生します。この用語は、実は名称としては正しくありません。なぜなら、メモリは物理的にコンピュータから失われているわけではないからです。実際には、あるプログラムにメモリが割り当てられ、その後、プログラム ロジックに不備があるために、そのプログラムがメモリにアクセスできなくなるのです。
メモリ リーク検出プロセスを開始するには、次の手順を実行します。
注意 : | この手順説明では、JRA 記録完了後に、アプリケーションが停止されたことが前提となっています。アプリケーションを停止していない場合、手順 1 および手順 2 は省略できます。 |
java DemoLeak
フィオナは、Memory Leak Detector の起動時に開く [傾向] タブ (図 23-25) の分析を開始しました。
ヒント : | 傾向分析はデフォルトで実行されているはずです。実行されていない場合は、傾向分析ボタンの中にある開始ボタン (図 23-27) をクリックすることで起動できます。 |
傾向分析ページには、アプリケーションのオブジェクト タイプについて、メモリ使用量の傾向の統計値が表示されます。このデータは、ガベージ コレクションの最中に JVM によって収集されます。そのため、傾向が表示されるにはガベージ コレクションが少なくとも 2 回は実行されている必要があります。
DemoObject という名前のクラスの増大が最大であることが分かります。
これにより、[タイプ] タブ (図 23-29) が開きます。DemoObject はハッシュテーブルのエントリに格納されていることが分かります。
これで、DemoObject が確かにハッシュテーブルのエントリに格納されていることが分かります。次の手順は、これらの DemoObject を格納しているハッシュテーブル エントリを保持しているインスタンスについて詳しく調べることです。
アプリケーションのすべてのハッシュテーブル エントリが同じタイプのオブジェクトを指し示しているのではないため、表示する参照タイプを選択するためのポップアップが表示されます。
[タイプ] タブの下部に、DemoObject を参照する java.util.Hashtable$Entry インスタンスのリストが表示されます。
これにより、図 23-33 に示す [インスタンス] タブが開きます。
ハッシュテーブルに格納されている DemoObject は、DemoThread によって保持されていることが判明しました。これがメモリ リークの原因だと考えられます。
Oracle JRockit Mission Control ツールを使って収集した証拠から判断して、フィオナはシステムで生じている問題がメモリ リークであることを特定できただけでなく、どのオブジェクト タイプでメモリ リークが発生しているのかを正確に見つけることができました。特定を行うための最初の手掛かりとなったのは、JRA 記録を見てガベージ コレクションの期間が増大していることに気付き、それら長期化しているガベージ コレクションが発生しているタイプに注目したことでした。そしてその後 Memory Leak Detector を実行し、疑わしいタイプのうち実際にはどれがリーク源であるのかを突き止めました。彼女は、インスタンスと割り当て数が増加し続けていたタイプを見つけることができました。明らかに、これらがメモリを保持していたせいで、他のオブジェクトを割り当てるためにメモリを解放することができていませんでした。これが決め手となって、メモリ リークと、その発生場所が特定されました。
![]() ![]() ![]() |