Java ベースのエンタープライズ バックエンド アプリケーションを扱ったことがあるソフトウェア開発者なら、ユーザーまたはテスト エンジニアから次のような安っぽくて奇妙なエラー メッセージを受け取ったことがあるでしょう: java.lang.OutOfMemoryError: Java heap space。 この問題を理解するには、アルゴリズムの複雑さ、特に「空間」の複雑さというコンピュータ サイエンスの基礎に戻る必要があります。思い出していただければ、すべてのアプリケーションには最悪のケースの特性があります。具体的には、ストレージのサイズに関して、推奨よりも多くのストレージがアプリケーションに割り当てられることになり、これは予測不可能ですが深刻な問題です。これにより、ヒープ メモリが過剰に使用され、「メモリ不足」状態が発生しました。
この特定の状況で最悪なのは、アプリケーションが修復できず、クラッシュしてしまうことです。最大メモリ (-Xmx オプション) を使用しても、アプリケーションを再起動しようとすると、長期的な解決策にはなりません。ヒープ使用量が増大または膨らむ原因を理解しなければ、メモリ使用量の安定性 (したがってアプリケーションの安定性) は保証されません。では、メモリに関連するプログラミングの問題を理解するためのより効果的な方法は何でしょうか?メモリ不足が発生した場合にこの質問に答える唯一の方法は、アプリケーションのメモリ ヒープと分散を理解することです。 この前提に基づいて、私たちは以下の側面に焦点を当てます。
ヒープ分析の準備としてアプリケーションを構成する メモリ リークのような非決定的かつ散発的な問題は、事後に分析するのが困難です。したがって、メモリ オーバーフローに対処する最善の方法は、JVM 仮想マシンに JVM 仮想マシンのメモリ状態のヒープ ファイルをダンプさせることです。 Sun HotSpot JVM には、メモリ オーバーフローがファイルに発生したときに JVM にヒープ状態をダンプするように指示するメソッドがあります。標準形式は .hprof です。したがって、この操作を実現するには、JVM 起動項目に XX:+HeapDumpOnOutOfMemoryError を追加します。メモリオーバーフローが発生するまでに長い時間がかかる可能性があるため、実稼働システムでもこのオプションを追加する必要があります。 ヒープ ダンプ .hprof ファイルを特定のファイル システムの場所に書き込む必要がある場合は、ディレクトリ パスを XX:HeapDumpPath に追加します。アプリケーションが指定されたディレクトリ パスへの書き込みアクセス権を常に持っていることを確認してください。 原因分析 101: メモリ不足エラーの性質を理解する メモリ オーバーフロー エラーを評価して理解しようとする場合、最初に行うことはメモリ増加特性を観察することです。状況に応じて可能性を評価します。 スパイク型: このタイプのメモリ リークは、特定の種類の負荷がかかるとさらに激しくなります。 JVM が 20 人のユーザーにメモリを割り当てると、アプリケーションは正常に実行されます。ただし、100 人目のユーザーに達すると、メモリ ピークが発生し、メモリ オーバーフローが発生する可能性があります。この問題を解決するには 2 つの方法があります。 リーク: プログラミング上の問題により、メモリ使用量は時間の経過とともに徐々に増加します。 良好なガベージコレクションを備えた健全なグラフ 一定期間の健全性維持後の漏洩量の経時変化のグラフ メモリ使用量の急増やメモリリークを引き起こすメモリグラフ 使用量の急増を引き起こすメモリの問題の性質を理解したので、分析から得られた推論に基づいて、次の方法を使用してメモリ不足エラーを回避できます。 メモリの問題の解決 メモリ リークの原因となったコードの修正: アプリケーションが、参照 (実行中のアプリケーションからのオブジェクト参照) をクリアせずに、一定期間にわたってオブジェクトを段階的に追加したため、プログラム エラーを修正する必要がありました。たとえば、このエラーは、ビジネス オブジェクトが段階的に追加されるハッシュ テーブルに挿入しているが、ビジネス ロジックとトランザクションが完了後にこれらのオブジェクトを削除していない場合に発生する可能性があります。 修正として最大メモリ値を増やします。実行中のメモリ特性とヒープについて理解した後、推奨される最大メモリ値ではアプリケーションの安定性を満たすことができないため、メモリ オーバーフローを再度回避するために、割り当てられた最大ヒープ メモリを増やす必要がある場合があります。したがって、ヒープ プロファイラーの評価に基づいて、Java -Xmx フラグをより高い値に更新してアプリケーションを実行する必要がある場合があります。 ヒープ分析 以下では、ヒープ分析ツールを使用してヒープダンプを分析する方法を詳しく分析します。この例では、Eclipse Foundation のオープン ソース ツール MAT を使用します。 MATを使用したヒープ分析 もっと深く掘り下げてみましょう。ヒープ メモリ オーバーフローの例を取得し、分析について考えるために、MAT のさまざまな表現とビューを調べるのに役立つ一連の手順を実行します。 1. メモリ オーバーフロー エラーが発生したときに生成された .hprof ヒープ ファイルを開きます。 MATは多くのインデックスファイルを作成するので、ダンプファイルを専用のフォルダにコピーしてください:ファイル -> 開く 2. ダンプ ファイルを開くと、メモリ リーク疑いレポートとコンポーネント レポートのオプションがあります。 「疑わしいリークレポートの実行」を選択します。 3. リーク疑いテーブルを開くと、プレビュー ウィンドウの円グラフに、オブジェクトごとに保持されているメモリの分布が表示されます。メモリ内の上位オブジェクト (最も多く保持されているメモリを持つオブジェクト - 蓄積されたメモリと参照されたオブジェクト) が表示されます。 4. 上記の円グラフは、メモリ参照 (独自のメモリと合計メモリ) が最も多いオブジェクトを集計して、3 つの容疑者を示しています。 それぞれのケースを一つずつ見ていき、それがメモリ不足エラーの根本原因であるかどうかを評価してみましょう。 疑わしい点1 「<システム クラス ローダー>」によってロードされた「java.lang.ref.Finalizer」の 454,570 インスタンスは、790,205,576 (47.96%) バイトを占有しました。 これは、割り当てられたアプリケーション メモリの約 50% を占める JVM ファイナライザー インスタンスが 454,570 個あることを示しています。 読者が Java Finalizer の機能を知っていると仮定すると、上記の情報から何がわかるでしょうか? はじめに: http://stackoverflow.com/questions/2860121/why-do-finalizers-have-a-severe-performance-penalty 基本的に、開発者はインスタンスのリソースを解放するためにいくつかのカスタムファイナライザーを作成します。ファイナライザによって収集されたこれらのインスタンスは、別のキューを使用する JVM のガベージ コレクション アルゴリズムの範囲外です。実際、このパスはガベージ コレクション メカニズムのクリーンアップ パスよりも長くなります。では、これらのターミネータが実際に何を終了させるのかを考えてみましょう。 あるいは、sun.security.ssl.SSLSocketImpl の 20% を占めるポイント 2 が疑わしいのかもしれません。これらがファイナライザーによってファイナライズされるインスタンスであることを確認できますか? 疑わしい点2 ここで、MAT の上部にある [ツール] ボタンの下にある Dominator ビューを開きます。 MAT によって解析され、有効なヒープ ストレージを示す、リストされているすべてのクラス インスタンスが表示されます。 次に、Dominator ビューで、java.lang.Finalizer と sun.security.ssl.SSLSocketImpl の関係を理解しようとします。 sun.security.ssl.SSLSocketImpl 列を右クリックし、GC ルート -> ソフト/弱い参照を除外を開きます。 ここで、MAT はメモリ グラフの描画を開始し、GC ルートへのパスとそれに対応するインスタンス参照を表示します。これは別のページに表示され、次の引用が表示されます。 上記の参照チェーンに示されているように、インスタンス SSLSocketImpl は java.lang.ref.Finalizer から取得され、SSLSocketImpl インスタンス全体は約 88k を占有します。また、ファイナライザ チェーンは、次のインスタンスを指すリンク リスト データ構造であることにも気付きます。 結論: この時点で、Java ファイナライザーが SSLSocketImpl オブジェクトを収集しようとしていることがはっきりとわかります。なぜこれほど多くの情報がまだ収集されていないのかを解明するために、コードを調べ始めました。 コードを確認する コードをチェックして、ソケットが閉じられていることが原因かどうかを確認する必要があります。この場合、I/O に関連するすべてのストリームを適切に閉じる必要があることがわかります。ある時点で、JVM が原因であると疑いました。実は、Open JDK 6.0.XX の GC (ガベージ コレクター) のコードにバグがあります。 この記事が、Java アプリケーションのエラーがヒープ ストレージによって発生しているのか、内部の問題によって発生しているのかを分析するためのパターンを提供できたことを願っています。ヒーププロファイリングを楽しんでください! さらに読む 浅いヒープと保持されたヒープ 浅いヒープは、1 つのオブジェクトによって消費されるメモリです。状況に応じて、オブジェクトには 32 ビットまたは 64 ビット (OS アーキテクチャによって異なります)、Integer の場合は 4 バイト、Long の場合は 8 バイトなどが必要になります。ヒープ ダンプの形式に応じて、メモリ サイズ (たとえば、8 に揃える) を調整して、VM の実際の消費量をより適切に調整できます。 X の保持セットは、X がガベージ コレクションされたときに削除されるオブジェクトのセットです。 X の保持ヒープは、X の保持セット内のすべてのオブジェクトの浅いヒープの合計、つまり X によって保持されるメモリです。 一般的に言えば、オブジェクトの浅いヒープはヒープ内のサイズです。同じオブジェクトの保持サイズは、オブジェクトがガベージ コレクションされたときのヒープ メモリの合計量です。 特定のクラスのすべてのオブジェクト、特定のクラス ローダーによってロードされたすべてのクラスのすべてのオブジェクト、または任意のオブジェクトなど、一部の主要なオブジェクト セットには、その主要なセットのすべてのオブジェクトがアクセス不能になった場合に解放されるオブジェクトのセットである保持セットがあります。 予約セットには、これらのオブジェクトと、これらのオブジェクトを通じてのみ取得できるその他のオブジェクトが含まれます。保持セットのサイズは、保持セットに含まれるすべてのオブジェクトのヒープ サイズです。 |
<<: VMware の高可用性: サーバー仮想化におけるクラスター HA と FT
>>: 仮想化について語るパート 4 - コンピューティング仮想化における CPU 仮想化
Hizakura BV は、主に IPv4、V6 の売買およびリース サービスを提供しており、オラン...
DA International Group Ltd. の子会社である alphavps.bg は ...
リアルタイムの更新と迅速な意思決定が求められる世界では、データ応答の遅延は煩わしいものになる可能性が...
A5 電子商取引部門は最近、A5 淘宝網衣料品トピック http://www.admin5.net/...
著者は長年にわたり SEO 最適化に取り組んでおり、毎日インターネットを利用しています。ウェブサイト...
昨日、「JD.com の一般的なセカンダリ カテゴリ ページの SEO の簡単な分析」でスライドにつ...
グーグルや百度などの検索エンジンのアルゴリズムがここ数日継続的に調整されていることから判断すると、モ...
Baidu は新しい医療ウェブサイトを差別しているのですか? K ステーション? 必ずしもそうではあ...
PHOENIXVPSドメイン名は3月14日に登録されました。現在、正式にリリースされており、XENと...
SEO に関する非常に印象的なジョークがあります。「SEO とは何ですか? 答えは外部リンクを投稿す...
A5のウェブサイトでは毎日多くの著者がウェブサイト構築の経験を共有していますが、彼らの言葉には自信と...
1998 年に設立されたアメリカの老舗企業 Smarthost は、現在、3 つの独立したサーバーを...
クリスマスが近づいており、有名なホスティングブランドのgreengeeksもクリスマスプロモーション...
多くの Web サイトでは、ホームページへのリンクとして http://www.yourdomain...
[51CTO.com からのオリジナル記事] 2010 年に OpenStack オープンソース プ...