Q:在Java中怎麼可以產生記憶體外洩?
A:Java中,造成記憶體外洩的原因有很多種。典型的例子是一個沒有實作hasCode和
equals方法的Key類別在HashMap中保存的情況。最後會產生很多重複的物件。所有的內存洩露
最後都會拋出OutOfMemoryError異常,以下透過一段簡短的通過無限循環模擬記憶體洩露
的例子說明一下。
複製代碼代碼如下:
import java.util.HashMap;
import java.util.Map;
public class MemoryLeak {
public static void main(String[] args) {
Map<Key, String> map = new HashMap<Key, String>(1000);
int counter = 0;
while (true) {
// creates duplicate objects due to bad Key class
map.put(new Key("dummyKey"), "value");
counter++;
if (counter % 1000 == 0) {
System.out.println("map size: " + map.size());
System.out.println("Free memory after count " + counter
+ " is " + getFreeMemory() + "MB");
sleep(1000);
}
}
}
// inner class key without hashcode() or equals() -- bad implementation
static class Key {
private String key;
public Key(String key) {
this.key = key;
}
}
//delay for a given period in milli seconds
public static void sleep(long sleepFor) {
try {
Thread.sleep(sleepFor);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//get available memory in MB
public static long getFreeMemory() {
return Runtime.getRuntime().freeMemory() / (1024 * 1024);
}
}
結果如下:
複製代碼代碼如下:
map size: 1000
Free memory after count 1000 is 4MB
map size: 2000
Free memory after count 2000 is 4MB
map size: 1396000
Free memory after count 1396000 is 2MB
map size: 1397000
Free memory after count 1397000 is 2MB
map size: 1398000
Free memory after count 1398000 is 2MB
map size: 1399000
Free memory after count 1399000 is 1MB
map size: 1400000
Free memory after count 1400000 is 1MB
map size: 1401000
Free memory after count 1401000 is 1MB
.....
.....
map size: 1452000
Free memory after count 1452000 is 0MB
map size: 1453000
Free memory after count 1453000 is 0MB
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
在 java.util.HashMap.addEntry(HashMap.java:753)
at java.util.HashMap.put(HashMap.java:385)
at MemoryLeak.main(MemoryLeak.java:10)
Q:怎麼解決上面的記憶體外洩?
A:實作Key類別的equals和hasCode方法。
複製代碼代碼如下:
.....
static class Key {
private String key;
public Key(String key) {
this.key = key;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Key)
return key.equals(((Key) obj).key);
else
return false;
}
@Override
public int hashCode() {
return key.hashCode();
}
}
.....
重新執行程序會得到以下結果:
複製代碼代碼如下:
map size: 1
Free memory after count 1000 is 4MB
map size: 1
Free memory after count 2000 is 4MB
map size: 1
Free memory after count 3000 is 4MB
map size: 1
Free memory after count 4000 is 4MB
……
Free memory after count 73000 is 4MB
map size: 1
Free memory after count 74000 is 4MB
map size: 1
Free memory after count 75000 is 4MB
Q:在實際場景中,你怎麼查找記憶體外洩?
A:透過以下程式碼取得線程ID
複製代碼代碼如下:
C:/>jps
5808 Jps
4568 MemoryLeak
3860 Main
透過命令列打開jconsole
複製代碼代碼如下:
C:/>jconsole 4568
實作了hasCode和equals的Key類別和沒有實現的圖表如下所示:
沒有內存洩漏的:
造成內存外洩的: