まず知っておくべきこと
1. C/C++ プログラマはメモリを自分で管理しますが、Java メモリは GC によって自動的に再利用されます。
私は C++ にはあまり詳しくありませんが、おそらくこれに関して常識的な間違いを犯したわけではありません。
2.メモリリークとは何ですか?
メモリ リークとは、システム内にリサイクルできないメモリが存在することを指し、場合によってはメモリ不足やシステム クラッシュを引き起こすことがあります。
C/C++ では、割り当てられたメモリが解放されない場合にメモリ リークが発生します。
3. Java にはメモリ リークが存在します。議論を続ける前に、まずこれを認めなければなりません。 Javaにはメモリリークがありますが、特にコード自体にこだわらない人は基本的に気にする必要はありません。
Java でのメモリ リークは、ガベージ コレクターによってリサイクルできない無駄なオブジェクトが存在することを意味します。
また、メモリ リークの問題があったとしても、それが表示されない場合もあります。
4. Java のパラメータはすべて値によって渡されます。
基本型については基本的に異論はありませんが、参照型については異論がありません。
Javaのメモリリーク
1. ヒープ メモリ オーバーフロー (outOfMemoryError: Java ヒープ スペース)
JVM 仕様では、ヒープ内のメモリはオブジェクト インスタンスと配列の生成に使用されます。
細分化すると、ヒープ メモリをヤング世代とオールド世代に分けることもできます。ヤング世代には、Eden 領域と 2 つの Survivor 領域が含まれます。
新しいオブジェクトが生成されるとき、メモリ アプリケーション プロセスは次のようになります。
a. jvm はまず、新しいオブジェクトに必要なメモリを eden 領域に割り当てようとします。
b. メモリ サイズが十分な場合はアプリケーションが終了し、そうでない場合は次のステップに進みます。
c. JVM は youngGC を開始し、Eden 領域の非アクティブなオブジェクトを解放しようとします。解放後、Eden 領域がまだ新しいオブジェクトを配置するのに十分でない場合は、Eden 内のアクティブなオブジェクトの一部を Survivor 領域に配置しようとします。
d. Survivor エリアは、Eden と old の間の中間交換エリアとして使用されます。OLD エリアに十分なスペースがある場合、Survivor エリアのオブジェクトは Old エリアに移動され、そうでない場合は Survivor エリアに保持されます。
e. OLD 領域に十分なスペースがない場合、JVM は OLD 領域でフル GC を実行します。
f. フル GC の後、Survivor 領域と OLD 領域が Eden からコピーされた一部のオブジェクトをまだ格納できず、JVM が Eden 領域に新しいオブジェクト用のメモリ領域を作成できない場合、「メモリ不足エラー」が表示されます。
outOfMemoryError:Java ヒープ スペース
2. メソッド領域のメモリオーバーフロー (outOfMemoryError: permgem space)
JVM仕様では、メソッド領域には主にクラス情報、定数、静的変数などが格納されます。
したがって、プログラムがロードするクラスが多すぎる場合、またはリフレクションや gclib などの動的プロキシ生成テクノロジを使用すると、この領域でメモリ オーバーフローが発生する可能性があります。一般に、この領域でメモリ オーバーフローが発生すると、次のようなエラー メッセージが表示されます。
outOfMemoryError:permgem スペース
3. スレッド スタック オーバーフロー (java.lang.StackOverflowError)
スレッド スタックはスレッドに固有のメモリ構造であるため、スレッド スタックの問題はスレッドの実行中に生成されるエラーである必要があります。
一般に、スレッド スタック オーバーフローは、再帰が深すぎるか、メソッド呼び出しのレベルが多すぎることが原因で発生します。
スタック オーバーフローが発生した場合のエラー メッセージは次のとおりです。
ジャワ。ラング。スタックオーバーフローエラー
メモリ リークのいくつかのシナリオ:
1. 存続期間の長いオブジェクトは、存続期間の短いオブジェクトへの参照を保持します。
これはメモリ リークの最も一般的なシナリオであり、コード設計における一般的な問題です。
たとえば、ローカル変数がグローバル静的マップにキャッシュされており、クリア操作がない場合、マップは時間の経過とともにますます大きくなり、メモリ リークが発生します。
2. ハッシュセット内のオブジェクトのパラメータ値を変更します。パラメータはハッシュ値の計算に使用されるフィールドです。
オブジェクトが HashSet コレクションに格納された後は、ハッシュ値の計算に関与するオブジェクト内のフィールドを変更することはできません。変更しない場合、オブジェクトの変更されたハッシュ値は、最初に HashSet コレクションに格納されたときのハッシュ値とは異なります。この場合、contains メソッドがオブジェクトの現在の参照をパラメーターとして使用して HashSet コレクションからオブジェクトを取得しても、オブジェクトが見つからないという結果が返され、結果としてオブジェクトの取得も失敗します。 HashSet コレクションから現在のオブジェクトを削除すると、メモリ リークが発生します。
3. 接続数とマシンのシャットダウン時間を設定します
リソースを大量に消費する接続を長時間開いていると、メモリ リークが発生する可能性があります。
メモリ リークの例を見てみましょう。
パブリッククラススタック{プライベートオブジェクト[]要素=新しいオブジェクト[10];パブリックボイドプッシュ(オブジェクトe){サイズ++] = e; } size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity(){ if(elements.length == size){ Object[] oldElements = 要素; 要素 = 新しい Object[2 * 要素。長さ+1]; arraycopy(oldElements,0, 要素, 0, サイズ) } }}
上記の原理は非常に単純です。スタックに 10 個の要素が追加され、すべてがポップアウトされた場合、スタックは空であり、必要なものは何もありませんが、このオブジェクトはメモリ リークの 2 つの要件を満たします。状態:未使用のため、リサイクルできません。
しかし、そのようなものが存在しても、このスタックの使用が減れば、必ずしも何らかの結果が生じるとは限りません。
とにかく、私たちのメモリはすでに G に達しているので、それがどのような影響を与えるでしょうか? それに、これはすぐにリサイクルされるので、それはどうなるのでしょうか?以下に 2 つの例を見てみましょう。
例1
public class Bad{ public static Stack s=Stack();プッシュ(新しいオブジェクト()); Pop(); //ここのオブジェクトにメモリリークがあります。 Push(new Object()); //上記のオブジェクトはリサイクルできます。これは自己修復と同等です。}}
静的なのでプログラムが終了するまで存在しますが、自己修復機能があることもわかります。
つまり、スタックに最大 100 個のオブジェクトがある場合、最大でも 100 個のオブジェクトだけが内部的に保持され、それらがすべて役に立たないことは容易に理解できます。なぜなら、新しい進行状況を入力すると、以前の参照は自然に消えてしまうからです。
例 2
public class NotTooBad{ public void doSomething(){ Stack s=new Stack(); Push(new Object()); //その他のコード。 Pop();//これにより、オブジェクトがリサイクルできなくなり、メモリ リークが発生します。 }//メソッドを終了すると、sは自動的に無効になり、sは再利用でき、スタック内の参照も自然になくなるので、 //ここでも自己修復が可能であり、このメソッドには機能が無いと言えます。メモリリークの問題ですが、後で引き渡されます// 閉じられていて外部に公開されていないため、GC にのみ与えられます。上記のことが言えます。コード 99。 // 9999% の状況では影響はありません。 もちろん、そのようなコードを書いても悪影響はありませんが、 // 間違いなくゴミコードであると言えます。そこに 1 つ追加します。空の for ループは大きな影響を与えません。これを実行しますか?}
上記の 2 つの例はほんの些細なものですが、C/C++ でのメモリ リークは悪いものではなく、最悪のものです。
1 か所でリサイクルしないと、このメソッドを頻繁に呼び出すとメモリが使い果たされてしまいます。
Java には自己修復機能 (私が命名したものでまだ特許は申請していません) があるため、Java のメモリ リーク問題はほとんど無視できますが、知っている人は実行しないでください。
メモリ リークを回避するには、コードを記述するときに次の提案を参照してください。
1. 不要なオブジェクトへの参照をできるだけ早く解放します。
2. 文字列処理を使用し、String の使用を避け、StringBuffer を広範囲に使用します。各 String オブジェクトは独立したメモリ領域を占有する必要があります。
3. 静的変数はできるだけ使用しないようにします。静的変数は永続世代 (メソッド領域) に格納され、永続世代は基本的にガベージ コレクションに参加しません。
4. ループ内でオブジェクトを作成しないようにします。
5. 大きなファイルを開いたり、一度にデータベースから大量のデータを取得したりすると、メモリ オーバーフローが発生しやすくなります。そのため、このような場所では、最大データ量を大まかに計算し、必要な最小および最大メモリ スペースの値を設定する必要があります。