Java 言語で作成されたエンタープライズ・アプリケーションは、 複雑なオブジェクト関係を持ち、多数のオブジェクトを使用します。Java 言語はオブジェクトのライフ・サイクルに関連したメモリーを自動的に管理しますが、アプリケーションのオブジェクト使用パターンを理解することは重要です。
こうした管理技法を適用するには、ガーベッジ・コレクションの効果について理解する必要があります。
ガーベッジ・コレクションについて詳しくは、パフォーマンス: 学習用リソース を参照してください。
IBM サポートの資料を利用すると、 この問題の解決に必要な情報収集の時間を節約できます。 PMR を開く前に、IBM サポート・ページを参照してください。
Java ガーベッジ・コレクションをテストすることによって、 アプリケーションがどのようにメモリーを使用するかが理解できます。 ガーベッジ・コレクションは Java の強みです。アプリケーション作成者からメモリー管理の負担をなくす ことによって、Java アプリケーションは、ガーベッジ・コレクション機能を持たない言 語で作成されたアプリケーションよりも堅固になっています。ただし、この堅固さが生かされるのは、アプリケーションがオブジェクトを 過剰に使用していない場合に限ります。 ガーベッジ・コレクションは通常、適切に機能するアプリケーションの合計実行時間の 5% から 20% を消費します。管理を行わない場合、ガーベッジ・コレクションはアプリケーションにとって最大のボトルネックの 1 つになります。
Solaris オペレーティング環境でのガーベッジ・コレクションについての詳細は、 パフォーマンス: 学習用リソース を参照してください。
ガーベッジ・コレクションを使用して、アプリケーションのパフォーマンスの正常 性を評価することができます。固定ワークロードの実行中にガーベッジ・コレクションをモニターすることによって、アプリケーションがオブジェクトを使いすぎているかどうかがわかります。 ガーベッジ・コレクションによって、メモリー・リークがあるかどうか検出することもできます。
Tivoli Performance Viewer のオブジェクト統計 または verbose:gc JVM 構成設定を使用して、ガーベッジ・コレクション統計をモニターすることができます。verbose:gc フォーマットは、異なる JVM またはリリース・レベルの間では標準化されません。 IBM verbose:gc 出力の説明については、以下を参照してください。 パフォーマンス: 学習用リソース .
このタイプの検査を行う場合は、ヒープ・サイズの最小値と最大値を 同じ値にします。ユーザー・エラーなど、実稼働環境での使用にできるだけ一致する、繰り返 しの多い代表的なワークロードを選択します。
意味のある統計値を得るには、アプリケーションが定常状態になるまで固定ワークロードを実行します。 定常状態に達するまで、通常は数分かかります。
Tivoli Performance Viewer を使用して 、JVM ランタイムのカウンターを監視することによって、アプリケーションがオブジェクトを乱用しているかどうかを確認できます。Java 仮想マシン・プロファイラー・インターフェース (JVMPI) カウンターを 使用可能にするには、-XrunpmiJvmpiProfiler コマンド行オプションとともに 、JVM モジュールの最大レベルも設定しなければなりません。
ガーベッジ・コレクション間の平均時間は、最もよい結果が出た場合で、ガーベッジ・コレクション 1 回分の平均所要時間の少なくとも 5 から 6 倍です。この数値に達しない場合、アプリケーションがガーベッジ・コレクションに費やす時間は、実行時間の 15% を超えています。
ガーベッジ・コレクションのボトルネックを示す情報がある場合、 ボトルネックを解消する方法は 2 つあります。 アプリケーションを最適化する最も経済的な方法は、オブジェクト・キャッシュとプールをインプリメントすることです。 Java プロファイラーを使用して、ターゲットにするオブジェクトを 決定します。アプリケーションを最適化できない場合は、メモリー、プロセッサー、およびクローンの追加が役に立つことがあります。 メモリーを追加することによって、各クローンが妥当なヒープ・サイズを維 持できるようになります。プロセッサーを追加すると、 クローンを並列に実行できます。
メモリー・リークは、Java 言語ではガーベッジ・コレクションのボトルネックの原因となる危険な状況です。 メモリー・リークは最終的にシステムの不安定性につながるため、メモリーの過剰使 用よりも有害です。時間が経過するにつれて、ガーベッジ・コレクションが頻繁に発生し、 最終的にはヒープが使い尽くされて、Java コードは致命的なメモリー不足例外により停止します。 メモリー・リークは、使われていないオブジェクトが参照され、解放されないときに起こります。 メモリー・リークは、Hashtable などの集合クラスでよく発生します。 これは、この表が実際の参照情報が削除された後であっても、常にオブジェクトを参照していることが原因です。
ワークロードが高いと、アプリケーションが、実稼働環境でのデプロイメント直後に破損することが頻繁に起こります。このような状況は、リークを起こしているアプリケーションで、ワークロードの高さがリークの拡大を加速し、メモリー割り振りの失敗が生じる場合に特に顕著です。
メモリー・リークの問題は、一定の期間を経てからでないと表面化しないため、メモリー・リークは長期間実行するテストでは検出されやすいと言えます。 短期間の稼働テストでは誤った警告を受ける可能性があります。Java 言語では、いつメモリー・リークが発生しているかを認識しにくい場合があります。メモリーの使用が突然に、あるいは一定の期間内に単調に増加したように見える場合には、特に判断がむずかしいでしょう。 メモリー・リークの検出がむずかしいのは、このような増加が妥当なものであったり、開発者が意図的に行ったものであったりする可能性があるためです。 後になって使用されるオブジェクトと、まったく使用されないオブジェクトとを区別する方法は、アプリケーションを長期間実行していればわかってきます。 長期間実行するアプリケーションのテストでは、実際にオブジェクトが後になって使用されているかどうかについて、 より確信が持てるようになります。
多くの場合、同じテスト・ケースを続けて反復する場合に、メモリー・リークの問題が起こります。メモリー・リークのテストのゴー ルは、使用不能なメモリーと使用中のメモリーとの間で、相対的サイズに大きなギャッ プを作り出すことです。同じシナリオを何度も繰り返すことによって、このギャップは非常に累進的に増えていきます。このテストは、テスト・ケースの実行により発生するリーク数がごくわずかであるために、1 回の実行ではほとんど検出できないような場合に役立ちます。
反復テストは、システム・レベルまたはモジュール・レベルで使用できま す。モジュラー・テストの利点は、管理の点でより優れていることです。モジュールが、専用のモジュールを保持して、メモリーの使用など外部の副次作用が発生しないように設計されている場合、メモリー・リークのテストは簡単になります。最初に、モジュールを実行する前のメモリー使用量が記録されます。次に、固定セットのテスト・ケースが 繰り返し実行されます。テスト実行の終了時には、現在のメモリー使用量が記録され、顕著な変化がないかどうかチェックされます。実際のメモリー使用量を記録するときには、ガーベッジ・コレクションを行う必要があることを覚えておいてください。これを行うには、ガーベッジ・コレクションを実行したいモジュールに System.gc() を挿入するか、このイベントを強制的に発生させるプロファイル作成ツールを使用します。
一部のメモリー・リーク問題は、アプリ ケーションでいくつかのスレッドが実行中の場合にのみ起こる可能性があります。 残念なことに、プログラム・ロジックの複雑さが増したため、同期点からメモリー・リークが発生することが非常に多くなっています。プログラミング時に注意を怠ると、参照が保持されたままになったり、解放されなかったりする可能性があります。メモリー・リークの問題は、システムの並行性が 増すことによって、促進されたり、加速されたりすることがよくあります。 並行性を高める最も一般的な方法は、テスト・ドライバー内のクライアント数を増やすことです。
Tivoli Performance Viewer を使用すると、メモリー・リークの検出に役立ちます。
ヒープの消費が、ワークロードが高いときにリークが起こった可能性を示している (アプリケーション・サーバーの CPU 使用率が常に 100% 近くなっている) のに、その後、ワークロードが低いかほとんどアイドル状態になったときにはヒープが回復するように見えるのは、ヒープのフラグメント化が起きている兆候です。ヒープのフラグメント化は、JVM がガーベッジ・コレクションのサイクル中にメモリー割り振り要求を満たすために十分な量のオブジェクトを解放することができたとしても、ヒープ内の小さな空きメモリー域を圧縮して、連続した大きなスペースを作成する時間がない場合には、起こる可能性があります。
この他に、ヒープのフラグメント化は、小さなオブジェクト (512 バイト未満) が解放された場合にも起こります。オブジェクトが解放されても、ストレージは回復しません。この結果、ヒープ圧縮が実行されるまでは、メモリーのフラグメント化が進みます。
ヒープのフラグメント化は圧縮を強制するによって削減できますが、これを行う場合パフォーマンス・ペナルティーがあります。 Java -X コマンドを使用して、メモリー・オプションのリストを参照します。
Java ヒープ・パラメーターも、ガーベッジ・コレクションの動作に影響します。 ヒープ・サイズを増やすと、より多くのオブジェクトを作成することができます。大きなヒープは満杯になるまでに時間がかかるので、 ガーベッジ・コレクションが実行されるまでにアプリケーションが 稼働する時間が長くなります。 ただし、大きなヒープは圧縮にも時間がかかるので、ガーベッジ・コレクションの所要時間が長くなります。 ヒープ設定についての詳細は、Java 仮想マシンの調整 を参照してください。
パフォーマンス分析のためには、初期ヒープ・サイズと最大ヒープ・ サイズを等しくします。
Java アプリケーションの作業セット・サイズが不明な実動システムを調整する場合は、 初期ヒープ・サイズを、まず、最大ヒープ・サイズの 25% にするとよいでしょう。 これにより、JVM はヒープのサイズをアプリケーションの作業セットのサイズに適合させようとします。
この図は、3 つの CPU プロファイルを示し、 それぞれ固定ワークロードで Java ヒープ設定値を変えながら稼働しています。 中央のプロファイルでは、初期ヒープ・サイズと最大ヒープ・サイズは 128MB に設定されています。ガーベッジ・コレクションは 4 回行われています。ガーベッジ・コレクションの合計時間は、合計実行時間の約 15% です。 ヒープ・パラメーターを倍の 256MB にすると、一番上のプロファイルのように、 ガーベッジ・コレクション間の作業時間の長さが増えていきます。 ガーベッジ・コレクションは 3 回しか行われませんが、それぞれのガーベッジ・コレクションの長さも増大しています。3 番目のプロファイルでは、ヒープ・サイズが 64MB に削減され、逆の結果が示されています。ヒープ・サイズが小さくなると、ガーベッジ・コレクション間の時間と個々のガーベッジ・コレクションの時間は、どちらも短くなります。 これらの 3 つの構成すべてについて、ガーベッジ・コレクションの合計時間 は、約 15% です。 この例は、Java ヒープおよびそれとオブジェクト使用率との関係について重要な概念 を示しています。Java アプリケーションには、 常にガーベッジ・コレクションのコストがかかります。
ヒープのフリー・スペースが 85% 以上で安定している場合は、アプリケーション・サーバーおよびアプリケーションがヒープに割り振られたメモリーをあまり使用していないため、ヒープ・サイズの最大値を小さくすることを検討してください。
ローカル・モードで WebSphere Application Server wsadmin コマンド wsadmin -conntype none を使用している場合は、このコマンドを実行する前に、config_consistency_check プロパティーを false に設定する必要があります。