ホームページ  >  記事  >  Java  >  Javaのメモリリークとメモリオーバーフローとは何ですか

Javaのメモリリークとメモリオーバーフローとは何ですか

青灯夜游
青灯夜游オリジナル
2021-09-22 17:48:3615579ブラウズ

メモリ リークとは、プログラムがメモリを申請した後、割り当てられたメモリ領域を解放できないことを意味します。メモリ オーバーフローとは、プログラムがメモリを申請したときに、申請者が使用できる十分なメモリがないこと、または int データを保存するためのストレージ スペースが提供されているにもかかわらず、長いデータが保存され、その結果メモリが不足していることを意味します。 OOM エラーが報告されます。メモリ リークが蓄積すると、最終的にはメモリ オーバーフローが発生します。

Javaのメモリリークとメモリオーバーフローとは何ですか

#このチュートリアルの動作環境: Windows7 システム、Java8 バージョン、DELL G3 コンピューター。


1. メモリ リーク:

は、プログラムがメモリを適用した後、適用されたメモリ空間を解放できないことを意味します。これは大きな影響を及ぼしますが、メモリ リークが蓄積するとメモリ オーバーフローが発生します。

2. メモリ不足によるメモリ オーバーフロー:

は、プログラムがメモリを申請したときに、申請者が使用できる十分なメモリがないこと、つまり、メモリが不足していることを意味します。 、 int 型データのストレージ領域の一部が提供されますが、long 型データを格納すると、結果としてメモリが不足し、いわゆるメモリ オーバーフローである OOM エラーが報告されます。

3. 2 つの関係:

  • メモリ リークの蓄積は、最終的にメモリ オーバーフローにつながります

  • メモリ オーバーフローとは、必要なメモリ領域がシステムによって実際に割り当てられた領域を超えていることを意味します。このとき、システムはニーズを満たすことができないことと同じであり、メモリ オーバーフロー エラーが報告されます。

  • メモリ リークとは、使用するメモリ (新規) の割り当てをシステムに適用するが、使用後にメモリを返さない (削除) ことを意味します。その結果、メモリも失われます。申請したメモリにアクセスできなくなり (おそらくアドレスを失った可能性があり)、システムはそのメモリを必要なプログラムに再度割り当てることができません。鍵付きキャビネットをレンタルするのと同じで、物を保管して施錠した後、鍵を紛失したり返却しなかったりすると、そのキャビネットは誰も使用できなくなり、ゴミに出すこともできなくなります。彼に関する情報が見つからないため、リサイクルします。

  • メモリ オーバーフロー: さまざまな方法で皿にフルーツを 4 つしか置くことができません。果物を 5 つ置きましたが、地面に落ちて食べられませんでした。これがオーバーフローです。たとえば、スタックがいっぱいのときにスタックをプッシュすると、必然的にスペース オーバーフローが発生し、これをオーバーフローといいます。スタックが空の場合、スペース オーバーフローが発生します。これをアンダーフローといいます。 。つまり、割り当てられたメモリは一連のデータ項目を保持するのに十分ではありません。これはメモリ オーバーフローと呼ばれます。はっきり言ってそこまでは耐えられないのでエラー報告させていただきます。

#4. メモリ リークの分類 (発生方法による分類)

  • 一般的なメモリ リーク。メモリ リークのあるコードは複数回実行されるため、実行されるたびにメモリ リークが発生します。

  • 時々メモリ リークが発生します。メモリ リークを引き起こすコードは、特定の状況または操作下でのみ発生します。頻繁と散発は相対的なものです。特定の状況では、たまにしか起こらないことが一般的になる場合があります。したがって、メモリ リークを検出するには、テスト環境とテスト方法が重要です。

  • 1 回限りのメモリ リーク。メモリ リークを引き起こすコードは 1 回だけ実行されるか、アルゴリズムの欠陥により、常に 1 つのみのメモリ ブロックがリークされます。たとえば、クラスのコンストラクターでメモリが割り当てられているが、デストラクターでメモリが解放されていない場合、メモリ リークは 1 回だけ発生します。

  • #暗黙的なメモリ リーク。プログラムは実行中に継続的にメモリを割り当てますが、最後までメモリは解放されません。厳密に言えば、プログラムは要求されたすべてのメモリを最終的に解放するため、ここでメモリ リークは発生しません。しかし、数日、数週間、さらには数か月にわたって実行する必要があるサーバー プログラムの場合、メモリを時間内に解放しないと、最終的にシステムのメモリがすべて使い果たされる可能性があります。したがって、このタイプのメモリ リークを暗黙的メモリ リークと呼びます。
5. メモリオーバーフローの原因と解決策:

(1) メモリオーバーフローの原因:

    一度にデータベースから大量のデータを取得するなど、メモリにロードされるデータの量が大きすぎます。
  • 次の参照があります。コレクション クラス内のオブジェクト。後で空にされないため、JVM がリサイクルできなくなります。
  • コード内に無限ループがあるか、ループによって生成される重複オブジェクト エンティティが多すぎます。 ;
  • サードパーティ製ソフトウェアのバグを使用しました;
  • 起動パラメータのメモリ値の設定が小さすぎます
(2) メモリ オーバーフローの解決策 解決策:

最初のステップは、JVM 起動パラメータを変更し、メモリを直接増やすことです。 (-Xms および -Xmx パラメーターを必ず追加してください。)

2 番目のステップは、エラー ログをチェックして、「OutOfMemory」エラーの前に他の例外またはエラーがあるかどうかを確認することです。

3 番目のステップは、コードを詳しく調べて分析し、メモリ オーバーフローが発生する可能性のある場所を特定することです。

次の点に注目してください:

  • データベース クエリにすべてのデータを取得するクエリがあるかどうかを確認します。一般に、10 万件のレコードが一度にメモリにフェッチされると、メモリ オーバーフローが発生する可能性があります。この問題は比較的隠されています。オンラインになる前はデータベース内のデータが少なく、問題が発生する可能性は低かったのですが、オンラインになった後はデータベース内のデータが増え、1 つのクエリでメモリ オーバーフローが発生する可能性があります。したがって、データベース クエリにはページングを使用するようにしてください。

  • コード内に無限ループや再帰呼び出しがないか確認してください。

  • 新しいオブジェクト エンティティを繰り返し生成する大規模なループが存在するかどうかを確認します。

  • データベースクエリに全データを一度に取得するクエリがあるか確認してください。一般に、10 万件のレコードが一度にメモリにフェッチされると、メモリ オーバーフローが発生する可能性があります。この問題は比較的隠されています。オンラインになる前はデータベース内のデータが少なく、問題が発生する可能性は低かったのですが、オンラインになった後はデータベース内のデータが増え、1 つのクエリでメモリ オーバーフローが発生する可能性があります。したがって、データベース クエリにはページングを使用するようにしてください。

  • List や MAP などのコレクション オブジェクトが使用後に消去されていないか確認してください。 List や MAP などのコレクション オブジェクトには常にオブジェクトへの参照があるため、これらのオブジェクトは GC によってリサイクルできません。

#4 番目のステップは、メモリ表示ツールを使用してメモリ使用量を動的に表示することです

JVM8 メモリ モデル

メモリ オーバーフローの 10 のシナリオ

JVM の実行時には、まずクラス ローダー (classLoader) を使用して、次のバイトコード ファイルをロードする必要があります。必要なクラス。ロード後、実行のために実行エンジンに渡されますが、実行プロセス中には、データを保存するための一定期間のスペースが必要になります (CPU やメイン メモリに似ています)。このメモリ空間の割り当てと解放のプロセスは、注意が必要なランタイム データ領域です。メモリ オーバーフローは、クラス ローダーのロード中に発生します。メモリ オーバーフローは、OutOfMemoryError と StackOverflowError の 2 つのカテゴリに分類されます。以下に 10 個のメモリ オーバーフローの状況をリストし、サンプル コードを通じてメモリ オーバーフローがどのように発生するかを説明します。

1.java ヒープ メモリ オーバーフロー

java.lang.OutOfMemoryError:Java ヒープ スペース例外が発生すると、ヒープ メモリがオーバーフローします。

1)、問題の説明

  • #jvm メモリ セットが小さすぎ、オブジェクトに必要なメモリが大きすぎます。オブジェクトの作成時に割り当てられるため、この例外がスローされます。

  • トラフィック/データのピーク時には、特定のユーザー数や特定のデータ量など、アプリケーション自体の処理に特定の制限があります。ユーザー数またはデータ量が突然急増し、予想されるしきい値を超えると、ピークが停止する前に正常に実行されていた操作が停止し、java.lang.OutOfMemoryError: Java Heap Space Error

    ## がトリガーされます。

    ##2)、サンプル コード
次のコードをコンパイルし、実行中に jvm パラメータを -Xms20m -Xmx20m に設定します

上記例 リクエストのみの場合 1回5mのメモリを確保した場合、リクエスト量は少なくガベージコレクションは正常に行われエラーは発生しませんが、一度同時実行が発生するとメモリの最大値を超えてメモリオーバーフローが発生します投げられるだろう。

3. 解決策

まず、コードに問題がない場合は、2 つの jvm パラメーター -Xms と -Xmx を適切に調整し、ストレス テストを使用してこれらを調整します。最適値を達成するための 2 つのパラメータ。

2 番目に、ファイルのアップロードやデータベースからの大きなバッチの取得など、大きなオブジェクトのアプリケーションを避けるようにしてください。これは避ける必要があります。チャンクまたはバッチで処理するようにしてください。これは、通常の安定した処理に役立ちます。システムの実行。

最後に、リクエストの実行速度を上げてみます。ガベージ コレクションは早ければ早いほど良いです。そうしないと、大量の同時実行が発生したときに、新しいリクエストがメモリを割り当てることができなくなり、システムの雪崩を容易に引き起こします。

2、Java ヒープ メモリ リーク

1)、問題の説明

Java のメモリ リークは、次のようなオブジェクトです。 not アプリケーションによって再度使用されますが、ガベージ コレクションによって認識されません。したがって、これらの未使用のオブジェクトは Java ヒープ領域に無期限に存在し続けます。一定の蓄積により、最終的には java.lang.OutOfMemoryError がトリガーされます。

2)、サンプル コード

上記のコードを実行すると、純粋なキャッシュ ソリューションが基になるスケールを 10,000 にマッピングするだけであると仮定すると、問題なく永久に実行されることが期待されるかもしれません。 HashMap にすでに存在するすべてのキーの代わりに要素を追加します。ただし、キー クラスはそのquals() メソッドをオーバーライドしないため、実際には要素は追加され続けます。

漏洩したコードは継続的に使用されるため、時間の経過とともに「キャッシュされた」結果が大量の Java ヒープ領域を消費することになります。リークしたメモリがヒープ領域内のすべての使用可能なメモリを埋めると、ガベージ コレクションでクリーンアップできなくなり、java.lang.OutOfMemoryError が発生します。

3)、解決策

対応する解決策は比較的単純で、equals メソッドを書き直すだけです:

3. ガベージ コレクション タイムアウトのメモリ オーバーフロー

1)、問題の説明: アプリケーションが使用可能なメモリをすべて使い果たし、GC オーバーヘッド制限がエラーを超え、GC がエラーのクリアに複数回失敗すると、java.lang がトリガーされます。 JVM が GC の実行に多くの時間を費やしても効果はほとんどなく、GC プロセス全体が制限を超えると、エラーがトリガーされます (デフォルトの JVM 構成の GC 時間は 98% を超え、リサイクルされたヒープ メモリは 2 未満です) %)。

2)、サンプル コード

3)、解決策

オブジェクトのライフサイクルを短縮するには、ガベージ コレクションを迅速に実行してみてください。

4. メタスペース メモリ オーバーフロー

1)、問題の説明

メタスペースがオーバーフローすると、システムは Java をスローします。 .lang.OutOfMemoryError: メタスペース。この異常な問題の原因は、システムに大量のコードがあるか、多くのサードパーティ パッケージを参照しているか、動的コード生成とクラス ロードが使用されており、その結果、メタスペース内で大きなメモリ フットプリントが発生することです。

2)、サンプル コード

3)、解決策

デフォルトでは、メタスペースのサイズはローカル メモリによってのみ制限されます。ただし、マシン全体のパフォーマンスの観点から、この項目はマシン全体のサービスダウンを引き起こさないようにできる限り設定する必要があります。

  • 他の JVM プロセスに影響を与えないようにパラメーター構成を最適化します

-XX:MetaspaceSize、初期スペース サイズ、この値が指定された場合にガベージ コレクションがトリガーされますタイプのアンロードを実行すると、GC が値を調整します。大量のスペースが解放されると、値は適切に下げられます。解放されるスペースが少量の場合は、値を超えない限り、値は適切に増加します。 MaxMetaspaceSize。

-XX:MaxMetaspaceSize、最大スペース。デフォルトでは制限はありません。

上記の 2 つのサイズ指定オプションに加えて、2 つの GC 関連属性があります: -XX:MinMetaspaceFreeRatio. GC 後、メタスペースの最小残りスペース容量の割合が、割り当てられたスペースまで減ります。コレクション。 -XX:MaxMetaspaceFreeRatio、GC 後、メタスペースの最大残りスペース容量のパーセンテージが、スペースの解放によって発生するガベージ コレクションに減らされます。

  • サードパーティ パッケージを慎重に参照してください

サードパーティ パッケージについては、慎重に選択し、不要なパッケージを削除してください。これは、コンパイルとパッケージ化の速度を向上させるだけでなく、リモート展開の速度も向上します。

  • クラスを動的に生成するフレームワークに重点を置く

動的に生成されるクラスを多数使用するフレームワークについては、ストレス テストを行って検証する必要があります。動的に生成されたクラス メモリ要件を超えると、例外がスローされます。

5. ダイレクトメモリオーバーフロー

1) 問題の説明

ByteBuffer で assignDirect() を使用する場合に使用されます。多くの javaNIO (netty など) フレームワークは他のメソッドとしてカプセル化されており、この問題が発生すると、java.lang.OutOfMemoryError: ダイレクト バッファ メモリ例外がスローされます。

ByteBuffer の assignDirect メソッドをクリアせずに直接または間接的に使用すると、同様の問題が発生します。

2)、サンプル コード

3)、解決策

同様の操作を頻繁に行う場合は、パラメーターの設定を検討してください: -XX: MaxDirectMemorySize を設定し、時間内にメモリをクリアします。

6. スタック メモリ オーバーフロー

1)、問題の説明

スレッドが Java メソッドを実行すると、JVM新しいスタック フレームが作成され、スタックの先頭にプッシュされます。このとき、新しいスタック フレームが現在のスタック フレームとなり、メソッドの実行時にパラメータ、ローカル変数、中間命令などのデータを格納するためにスタック フレームが使用されます。

メソッドがそれ自体を再帰的に呼び出すと、新しいメソッドによって生成されたデータ (新しいスタック フレームとしても理解できます) がスタックの先頭にプッシュされます。メソッドがそれ自体を呼び出すたびに、現在のメソッドのデータがスタックにプッシュされます。したがって、再帰の各レベルでは、新しいスタック フレームを作成する必要があります。その結果、スタック内のメモリが再帰呼び出しによって消費され、再帰呼び出しが 100 万回行われると、100 万個のスタック フレームが生成されます。これにより、スタック メモリのオーバーフローが発生します。

2)、サンプル コード

3)、解決策

実際にプログラム内に再帰呼び出しがあり、スタック オーバーフローが発生した場合は、次の値を増やすことができます。 -Xss サイズにより、スタック メモリのオーバーフローの問題を解決できます。再帰呼び出しにより無限ループの形成が防止されます。そうしないと、スタック メモリのオーバーフローが発生します。

7. ローカルスレッド作成時のメモリオーバーフロー

1) 問題の説明

スレッドは基本的にローカルスレッド以外のメモリ領域のみを占有します。このエラーは、ヒープ以外のメモリ領域をスレッドに割り当てることができないことを示します。これは、メモリ自体が十分ではないか、ヒープ領域が大きく設定されすぎてメモリがあまり残っていないためです。 、そしてスレッドのせいでメモリ自体を消費するので十分ではありません。

2)、サンプル コード

3)、解決策

最初に、オペレーティング システムにスレッド数の制限があるかどうかを確認します。 、シェルを使用する スレッドも作成できません。これが問題である場合は、システムがサポートできるファイルの最大数を調整する必要があります。

日々の開発では、スレッドの最大数が制御可能であることを確認し、スレッド プールを恣意的に使用しないようにしてください。際限なく成長することはできません。

8. スワップ領域を超えたメモリ オーバーフロー

1) 問題の説明

Java アプリケーションの起動プロセス中, 必要なメモリは、-Xmx およびその他の同様の起動パラメータによって制限できます。 JVM によって要求された合計メモリが利用可能な物理メモリよりも大きい場合、オペレーティング システムはメモリからハード ディスクへのコンテンツの変換を開始します。

一般に、JVM はスワップ領域不足エラーをスローします。これは、アプリケーションが JVM ネイティブ ヒープにメモリを割り当てるように要求できず、ネイティブ ヒープが枯渇しそうになった場合に、エラー メッセージに次の内容が含まれることを意味します。割り当て失敗のサイズ (ワードセクション) とリクエスト失敗の理由。

2)、解決策

システムのスワップ領域のサイズを増やします。個人的には、スワップ領域を使用するとパフォーマンスが大幅に低下すると思います。この方法は推奨されません。最大メモリはシステムの物理メモリを超えます。次に、システム スワップ領域を削除し、アプリケーションのパフォーマンスを確保するためにシステム メモリのみを使用します。

9. 配列オーバーフロー メモリ オーバーフロー

1) 問題の説明 この種のメモリ オーバーフローの説明が発生することがあります 要求された配列サイズが VM の制限を超えています一般に、Java には、アプリケーションが割り当てることができる配列の最大サイズに制限があります。この制限はプラットフォームによって異なりますが、通常は 10 ~ 21 億要素です。 「要求された配列サイズが VM 制限を超えています」エラーが発生する場合、アプリケーションが Java 仮想マシンがサポートできるよりも大きい配列を割り当てようとしていることを意味します。 JVM は配列にメモリを割り当てる前に、割り当てられたデータ構造がこのプラットフォームでアドレス指定可能かどうかというプラットフォーム固有のチェックを実行します。

2)、サンプルコード

以下は配列が上限を超えているコードです。

3)、解決策

したがって、配列の長さはプラットフォームで許可されている長さの範囲内である必要があります。ただし、主に Java 配列のインデックスが int 型であるため、このエラーが発生することは一般にまれです。 Java の最大の正の整数は 2^31 - 1 = 2,147,483,647 です。また、プラットフォーム固有の制限はこの数値に非常に近い可能性があります。たとえば、私の環境 (64 ビット macOS、Jdk1.8 を実行) では、最大 2,147,483,645 (Integer.MAX_VALUE-2) の長さの配列を初期化できます。配列の長さが 1 増加して nteger.MAX_VALUE-1 に達すると、OutOfMemoryError が発生します。

10. システムがプロセス メモリ オーバーフローを強制終了します

1). 問題の概要. 問題を説明する前に、まず、オペレーティング システム: オペレーティング システムはプロセスの概念に基づいて構築されています。これらのプロセスはカーネル内で動作します。「メモリ不足キラー」と呼ばれる非常に特殊なプロセスがあります。システムのメモリが不十分であることをカーネルが検出すると、OOM キラーがアクティブになり、現在最も多くのメモリを占有しているのが誰かを確認して、プロセスを強制終了します。

通常、「メモリ不足: プロセスを強制終了するか子を犠牲にする」エラーは、使用可能な仮想仮想メモリ (スワップ領域を含む) がオペレーティング システム全体が危険にさらされるまで消費されるとトリガーされます。この場合、OOM Killer は「不正プロセス」を選択して強制終了します。

2)、サンプル コード

3)、解決策

スワップ領域を追加すると Java ヒープ領域例外を軽減できますが、それでも最善の解決策は、Java アプリケーションで十分なメモリを使用できるようにシステム メモリをアップグレードし、この問題が発生しないようにすることをお勧めします。

まとめ: 上記の 10 種類のメモリ オーバーフローの状況を通じて、誰もが実際に問題に遭遇したときの解決方法を理解できるようになります。また、実際のコーディングでは次のことも覚えておく必要があります。

#サードパーティの jar パッケージは慎重に導入する必要があり、コンパイル速度とシステム メモリの使用率を向上させるために、不要な jar パッケージは断固として削除する必要があります。
  • 大きなオブジェクトまたは多数のメモリ アプリケーションの場合は、最適化を実行する必要があります。処理パフォーマンスを向上させ、オブジェクトのライフ サイクルを短縮するために、大きなオブジェクトはスライスで処理する必要があります。

  • スレッドによって占有されるメモリが制御可能であることを確認するために、スレッドの数を固定するようにしてください。多数のスレッドが必要な場合、オープン接続の最大数が制限されます。オペレーティング システムを最適化する必要があります。
  • 再帰呼び出しの場合、再帰のレベルも制御する必要があります。高すぎたりスタックの深さを超えたりしないようにする必要があります。

  • スタックに割り当てられるメモリは大きければ大きいほど良いわけではありません。スタック メモリが大きければ大きいほど、スレッドの数が多くなり、ヒープ用のスペースがあまり残らなくなるためです。そして OOM を投げるのは簡単です。通常、JVM のデフォルトパラメータ (再帰を含む) には問題はありません。

推奨される関連ビデオ チュートリアル: Java ビデオ チュートリアル

以上がJavaのメモリリークとメモリオーバーフローとは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。