Q: Java ではどのようにしてメモリ リークが発生するのでしょうか?
A: Java では、メモリ リークの原因は数多くあります。典型的な例は、hasCode を実装していないコードであり、
Equals メソッドの Key クラスは HashMap に保存されます。最終的には、多数の重複オブジェクトが生成されます。すべてのメモリリーク
最終的に、OutOfMemoryError 例外がスローされます。これは、無限ループによるメモリ リークの簡単なシミュレーションです。
例を挙げて説明します。
次のようにコードをコピーします。
java.util.HashMapをインポートします。
java.util.Mapをインポートします。
パブリック クラス MemoryLeak {
public static void main(String[] args) {
Map<Key, String> map = new HashMap<Key, String>(1000);
整数カウンタ = 0;
while (true) {
// 不正な Key クラスにより重複オブジェクトを作成します
map.put(new Key("ダミーキー"), "値");
カウンタ++;
if (カウンター % 1000 == 0) {
System.out.println("マップ サイズ: " + map.size());
System.out.println("カウント後にメモリを解放する " + counter
+ " は " + getFreeMemory() + "MB");
睡眠(1000);
}
}
}
// hashcode() または equals() のない内部クラス キー -- 不正な実装
静的クラスのキー {
秘密文字列キー。
公開鍵(文字列キー) {
this.key = キー;
}
}
//ミリ秒単位で指定された期間遅延します
public static void sleep(long sleepFor) {
試す {
Thread.sleep(sleepFor);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 利用可能なメモリを MB 単位で取得します
public static long getFreeMemory() {
runtime.getRuntime().freeMemory() / (1024 * 1024) を返します。
}
}
結果は次のとおりです。
次のようにコードをコピーします。
マップサイズ: 1000
1000カウント後の空きメモリは4MBです
マップサイズ: 2000
カウント 2000 以降の空きメモリは 4MB です
マップサイズ: 1396000
カウント 1396000 以降の空きメモリは 2MB です
マップサイズ: 1397000
カウント 1397000 以降の空きメモリは 2MB です
マップサイズ: 1398000
カウント 1398000 以降の空きメモリは 2MB です
マップサイズ: 1399000
カウント 1399000 以降の空きメモリは 1MB です
マップサイズ: 1400000
カウント 1400000 以降の空きメモリは 1MB です
マップサイズ: 1401000
カウント 1401000 以降の空きメモリは 1MB です
……
……
マップサイズ: 1452000
カウント 1452000 以降の空きメモリは 0MB です
マップサイズ: 1453000
カウント 1453000 以降の空きメモリは 0MB です
スレッド「メイン」java.lang.OutOfMemoryError での例外: Java ヒープ スペース
java.util.HashMap.addEntry(HashMap.java:753) で
java.util.HashMap.put(HashMap.java:385) で
MemoryLeak.main(MemoryLeak.java:10) で
Q: 上記のメモリリークを解決するにはどうすればよいですか?
A: Key クラスの equals メソッドと hasCode メソッドを実装します。
次のようにコードをコピーします。
……
静的クラスのキー {
秘密文字列キー。
公開鍵(文字列キー) {
this.key = キー;
}
@オーバーライド
public booleanquals(Object obj) {
if (キーのオブジェクトインスタンス)
return key.equals(((Key) obj).key);
それ以外
false を返します。
}
@オーバーライド
public int hashCode() {
key.hashCode() を返します。
}
}
……
プログラムを再実行すると、次の結果が得られます。
次のようにコードをコピーします。
マップサイズ: 1
1000カウント後の空きメモリは4MBです
マップサイズ: 1
カウント 2000 以降の空きメモリは 4MB です
マップサイズ: 1
カウント 3000 後の空きメモリは 4MB です
マップサイズ: 1
カウント 4000 後の空きメモリは 4MB です
...
カウント 73000 以降の空きメモリは 4MB です
マップサイズ: 1
カウント 74000 以降の空きメモリは 4MB です
マップサイズ: 1
カウント 75000 以降の空きメモリは 4MB です
Q: 実際のシナリオでは、メモリ リークをどのように見つけますか?
A: 次のコードを通じてスレッド ID を取得します。
次のようにコードをコピーします。
C:/>jps
5808JPS
4568 メモリリーク
3860メイン
コマンドラインから jconsole を開きます
次のようにコードをコピーします。
C:/>jコンソール 4568
hasCodeとequalsを実装するKeyクラスとそれを実装しないチャートは以下のとおりです。
メモリリークなし:
メモリリークの原因: