Java弱引用與WeakHashMap
在《Effective Java 2nd Edition》中,第6條“消除過期的物件引用”提到,雖然Java有 垃圾回收機制,但是隻要是自己管理的記憶體,就應該警惕記憶體洩露的問題,例如的物件池、快取中的過期物件都有可能引發記憶體洩露的問題。書中還提到可以用 WeakHashMap來作為快取的容器可以有效解決這一問題。之前也確實遇到過類似問題,但是沒有接觸過“弱引用”相關的問題,於是查閱了一些資料。
《Java 理論與實踐: 用弱引用堵住記憶體洩漏》一文也指出了使用全域性的Map作為快取容器時發生的記憶體洩露問題,介紹瞭如何使用hprof工具來找出記憶體洩露,並分析瞭如何使用 弱引用來防止記憶體洩露,還分析了WeakHashMap的關鍵程式碼,非常有參考價值。但是這篇文章遺漏了幾個很重要的需要注意的地方,也缺少一段實驗代 碼,本文將會做出適當補充。
1、四種引用
從JDK1.2版本開始,把物件的引用分為四種級別,從而使程式能更加靈活的控制物件的生命週期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。
強引用:平時我們程式設計的時候例如:Object object=new Object();那object就是一個強引用了。如果一個物件具有強引用,那就類似於必不可少的生活用品,垃圾回收器絕不會回收它。當記憶體空 間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足問題。
軟引用(SoftReference):如果一個物件只具有軟引用,那就類似於可有可物的生活用品。如果記憶體空間足夠,垃圾回收器就不會回收它,如果記憶體 空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體。 軟引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果軟引用所引用的物件被垃圾回收,Java虛擬機器就會把這個軟引用加入到與之關聯 的引用佇列中。
弱引用(WeakReference):如果一個物件只具有弱引用,那就類似於可有可物的生活用品。弱引用與軟引用的區別在於:只具有弱引用的物件擁有更 短暫的生命週期。在垃圾回收器執行緒掃描它 所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒, 因此不一定會很快發現那些只具有弱引用的物件。 弱引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果弱引用所引用的物件被垃圾回收,Java虛擬機器就會把這個弱引用加入到與之關聯 的引用佇列中。
虛引用(PhantomReference):“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定物件的生命週期。如果一個物件 僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。 虛引用主要用來跟蹤物件被垃圾回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用佇列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個物件時,如果發現它還有虛引用,就會在回收物件的記憶體之前,把這個虛引用加入到與之 關聯的引用佇列中。程式可以通過判斷引用佇列中是否已經加入了虛引用,來了解被引用的物件是否將要被垃圾回收。程式如果發現某個虛引用已經被加入到引用隊 列,那麼就可以在所引用的物件的記憶體被回收之前採取必要的行動。
2、WeakHashMap原始碼分析
WeakHashMap維護了一個ReferenceQueue,儲存了所有存在引用的Key物件。
private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
WeakHashMap.Entry<K,V>中並沒有儲存Key,只是將Key與ReferenceQueue關聯上了。
private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
private V value;
private final int hash;
private Entry<K,V> next;
Entry(K key, V value, ReferenceQueue<K> queue, int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
……
}
WeakHashMap中有一個私有的expungeStaleEntries()方法,會在大部分共有方法中被呼叫。這個方法會將ReferenceQueue中所有失效的引用從Map中去除。
private void expungeStaleEntries() {
Entry<K,V> e;
while ( (e = (Entry<K,V>) queue.poll()) != null) {
int h = e.hash;
int i = indexFor(h, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
e.next = null; // Help GC
e.value = null; // " "
size--;
break;
}
prev = p;
p = next;
}
}
}
3、幾個需要注意的地方
WeakHashMap的Key是弱引用,Value不是。
WeakHashMap不會自動釋放失效的弱引用,僅當包含了expungeStaleEntries()的共有方法被呼叫的時候才會釋放。
4、一個簡單的例子
public static void main(String args[]) {
WeakHashMap<String, String> map = new WeakHashMap<String, String>();
map.put(new String("1"), "1");
map.put("2", "2");
String s = new String("3");
map.put(s, "3");
while (map.size() > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
System.out.println("Map Size:"+map.size());
System.out.println(map.get("1"));
System.out.println(map.get("2"));
System.out.println(map.get("3"));
System.gc();
}
}
執行結果是:
Map Size:3
1
2
3
Map Size:2
null
2
3
Map Size:2
null
2
3
(一直迴圈)
要注意String的特殊性,“2”是被放在常量池中的,所以沒有被回收。
1、四種引用
從JDK1.2版本開始,把物件的引用分為四種級別,從而使程式能更加靈活的控制物件的生命週期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。
強引用:平時我們程式設計的時候例如:Object object=new Object();那object就是一個強引用了。如果一個物件具有強引用,那就類似於必不可少的生活用品,垃圾回收器絕不會回收它。當記憶體空 間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足問題。
軟引用(SoftReference):如果一個物件只具有軟引用,那就類似於可有可物的生活用品。如果記憶體空間足夠,垃圾回收器就不會回收它,如果記憶體 空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體。 軟引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果軟引用所引用的物件被垃圾回收,Java虛擬機器就會把這個軟引用加入到與之關聯 的引用佇列中。
弱引用(WeakReference):如果一個物件只具有弱引用,那就類似於可有可物的生活用品。弱引用與軟引用的區別在於:只具有弱引用的物件擁有更 短暫的生命週期。在垃圾回收器執行緒掃描它 所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒, 因此不一定會很快發現那些只具有弱引用的物件。 弱引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果弱引用所引用的物件被垃圾回收,Java虛擬機器就會把這個弱引用加入到與之關聯 的引用佇列中。
虛引用(PhantomReference):“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定物件的生命週期。如果一個物件 僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。 虛引用主要用來跟蹤物件被垃圾回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用佇列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個物件時,如果發現它還有虛引用,就會在回收物件的記憶體之前,把這個虛引用加入到與之 關聯的引用佇列中。程式可以通過判斷引用佇列中是否已經加入了虛引用,來了解被引用的物件是否將要被垃圾回收。程式如果發現某個虛引用已經被加入到引用隊 列,那麼就可以在所引用的物件的記憶體被回收之前採取必要的行動。
2、WeakHashMap原始碼分析
WeakHashMap維護了一個ReferenceQueue,儲存了所有存在引用的Key物件。
private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
WeakHashMap.Entry<K,V>中並沒有儲存Key,只是將Key與ReferenceQueue關聯上了。
private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
private V value;
private final int hash;
private Entry<K,V> next;
Entry(K key, V value, ReferenceQueue<K> queue, int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
……
}
WeakHashMap中有一個私有的expungeStaleEntries()方法,會在大部分共有方法中被呼叫。這個方法會將ReferenceQueue中所有失效的引用從Map中去除。
private void expungeStaleEntries() {
Entry<K,V> e;
while ( (e = (Entry<K,V>) queue.poll()) != null) {
int h = e.hash;
int i = indexFor(h, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
e.next = null; // Help GC
e.value = null; // " "
size--;
break;
}
prev = p;
p = next;
}
}
}
3、幾個需要注意的地方
WeakHashMap的Key是弱引用,Value不是。
WeakHashMap不會自動釋放失效的弱引用,僅當包含了expungeStaleEntries()的共有方法被呼叫的時候才會釋放。
4、一個簡單的例子
public static void main(String args[]) {
WeakHashMap<String, String> map = new WeakHashMap<String, String>();
map.put(new String("1"), "1");
map.put("2", "2");
String s = new String("3");
map.put(s, "3");
while (map.size() > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
System.out.println("Map Size:"+map.size());
System.out.println(map.get("1"));
System.out.println(map.get("2"));
System.out.println(map.get("3"));
System.gc();
}
}
執行結果是:
Map Size:3
1
2
3
Map Size:2
null
2
3
Map Size:2
null
2
3
(一直迴圈)
要注意String的特殊性,“2”是被放在常量池中的,所以沒有被回收。
相關文章
- Java引用型別與WeakHashMapJava型別HashMap
- 強引用、軟引用、弱引用與虛引用
- Java的強引用、軟引用、弱引用、虛引用Java
- Java中的弱引用Java
- Java四種引用包括強引用,軟引用,弱引用,虛引用。Java
- 理解Java的強引用、軟引用、弱引用和虛引用Java
- Java/Android中的強引用、軟引用、弱引用、虛引用JavaAndroid
- Java中的WeakHashMap與類示例JavaHashMap
- Java四大引用詳解:強引用、軟引用、弱引用、虛引用Java
- java的強引用、軟引用、弱引用、幻象引用,引用佇列總結Java佇列
- Java引用型別解析:掌握強引用、軟引用、弱引用和幻象引用的妙用Java型別
- Java常見知識點彙總(⑲)——Java中的強引用、弱引用、軟引用、虛引用Java
- 強引用、軟引用、弱引用、虛引用
- 強引用 軟引用 弱引用 虛引用
- 強引用,軟引用,弱引用,虛引用
- 十分鐘理解Java中的弱引用Java
- 【JVM】如何理解強引用、軟引用、弱引用、虛引用?JVM
- 好程式設計師Java學習路線分享Java弱引用的理解與使用程式設計師Java
- Java中強、軟、弱、虛四種引用詳解Java
- 垃圾回收(四)【弱引用】
- 強引用、軟引用、弱引用、幻象引用有什麼區別?
- 關於強引用、軟引用、弱引用、幻象引用,你該如何回答?
- 強引用、軟引用、弱引用、幻象引用再不理解就晚了
- Swift 4 弱引用實現Swift
- 從原始碼解析 Swift 弱引用原始碼Swift
- 分析ThreadLocal的弱引用與記憶體洩漏問題thread記憶體
- 死磕 java集合之WeakHashMap原始碼分析JavaHashMap原始碼
- Java中的引用與ThreadLocalJavathread
- python3學習筆記之 強引用和弱引用Python筆記
- 老徐和阿珍的故事:強引用、軟引用、弱引用、虛引用,傻傻分不清楚
- Java引用計數與實現Java
- 別再找了,一文徹底解析Java 中的弱引用(參考官網)Java
- 如何實現分類中新增弱引用屬性
- Java中方法引用與Lambda區別 - foojayJava
- WeakHashMap,原始碼解讀HashMap原始碼
- java中的引用與c中的指標Java指標
- Rust 程式設計影片教程(進階)——015_2 弱引用Rust程式設計
- Java中的引用Java
- 面試官問:ThreadLocal中的鍵為什麼是弱引用?面試thread