轉載來源:http://my.eoe.cn/ymcao/archive/3623.html
Java中的軟引用,弱引用和虛引用
在Android的圖檔進行中,碰到的一個非常普遍的問題便是OOM錯誤 為此網上也有很多例子,而在之前的一篇轉載裡 提到了ListView中加載圖檔的ImageLoader,而其中有一處,使用到了名為SoftPreference的類 這是Java中的一個類 也就是所謂的軟引用 在查詢了相關的資料以後 會發現SoftPreference的特性,非常适合用來處理OOM引起的問題:
SoftReference、Weak Reference和PhantomRefrence分析和比較
本文将談一下對SoftReference(軟引用)、WeakReference(弱引用)和PhantomRefrence(虛引用)的了解,這三個類是對heap中java對象的應用,通過這個三個類可以和gc做簡單的互動。
強引用:
除了上面提到的三個引用之外,還有一個引用,也就是最長用到的那就是強引用。例如:
Object o=new Object();
Object o1=o;
上面代碼中第一句是在heap堆中建立新的Object對象通過o引用這個對象,第二句是通過o建立o1到new Object()這個heap堆中的對象的引用,這兩個引用都是強引用.隻要存在對heap中對象的引用,gc就不會收集該對象.如果通過如下代碼:
o=null;
o1=null;
如果顯式地設定o和o1為null,或超出範圍,則gc認為該對象不存在引用,這時就可以收集它了。可以收集并不等于就一會被收集,什麼時候收集這要取決于gc的算法,這要就帶來很多不确定性。例如你就想指定一個對象,希望下次gc運作時把它收集了,那就沒辦法了,有了其他的三種引用就可以做到了。其他三種引用在不妨礙gc收集的情況下,可以做簡單的互動。
heap中對象有強可及對象、軟可及對象、弱可及對象、虛可及對象和不可到達對象。應用的強弱順序是強、軟、弱、和虛。對于對象是屬于哪種可及的對象,由他的最強的引用決定。如下:
String abc=new String("abc"); //1
SoftReference abcSoftRef=new SoftReference(abc); //2
WeakReference abcWeakRef = new WeakReference(abc); //3
abc=null; //4
abcSoftRef.clear();//5
第一行在heap對中建立内容為“abc”的對象,并建立abc到該對象的強引用,該對象是強可及的。
第二行和第三行分别建立對heap中對象的軟引用和弱引用,此時heap中的對象仍是強可及的。
第四行之後heap中對象不再是強可及的,變成軟可及的。同樣第五行執行之後變成弱可及的。
SoftReference(軟引用)
軟引用是主要用于記憶體敏感的高速緩存。在jvm報告記憶體不足之前會清除所有的軟引用,這樣以來gc就有可能收集軟可及的對象,可能解決記憶體吃緊問題,避免記憶體溢出。什麼時候會被收集取決于gc的算法和gc運作時可用記憶體的大小。當gc決定要收集軟引用是執行以下過程,以上面的abcSoftRef為例:
1、首先将abcSoftRef的referent設定為null,不再引用heap中的new String("abc")對象。
2、将heap中的new String("abc")對象設定為可結束的(finalizable)。
3、當heap中的new String("abc")對象的finalize()方法被運作而且該對象占用的記憶體被釋放, abcSoftRef被添加到它的ReferenceQueue中。
注:對ReferenceQueue軟引用和弱引用可以有可無,但是虛引用必須有,參見:
Reference(T paramT, ReferenceQueue<? super T>paramReferenceQueue)
被 Soft Reference 指到的對象,即使沒有任何 Direct Reference,也不會被清除。一直要到 JVM 記憶體不足且 沒有 Direct Reference 時才會清除,SoftReference 是用來設計 object-cache 之用的。如此一來 SoftReference 不但可以把對象 cache 起來,也不會造成記憶體不足的錯誤 (OutOfMemoryError)。我覺得 Soft Reference 也适合拿來實作 pooling 的技巧。
| |
SoftRefenrence sr = new SoftReference(obj);
| |
弱引用
當gc碰到弱可及對象,并釋放abcWeakRef的引用,收集該對象。但是gc可能需要對此運用才能找到該弱可及對象。通過如下代碼可以了明了的看出它的作用:
String abc=new String("abc");
WeakReference abcWeakRef = new WeakReference(abc);
abc=null;
System.out.println("before gc: "+abcWeakRef.get());
System.gc();
System.out.println("after gc: "+abcWeakRef.get());
運作結果:
before gc: abc
after gc: null
gc收集弱可及對象的執行過程和軟可及一樣,隻是gc不會根據記憶體情況來決定是不是收集該對象。
如果你希望能随時取得某對象的資訊,但又不想影響此對象的垃圾收集,那麼你應該用 Weak Reference 來記住此對象,而不是用一般的 reference。
A obj = new A();
| |
...
if (wr.get()==null) {
System.out.println("obj 已經被清除了 ");
} else {
System.out.println("obj 尚未被清除,其資訊是 "+obj.toString());
}
...
}
在此例中,透過 get() 可以取得此 Reference 的所指到的對象,如果傳回值為 null 的話,代表此對象已經被清除。
這類的技巧,在設計 Optimizer 或 Debugger 這類的程式時常會用到,因為這類程式需要取得某對象的資訊,但是不可以 影響此對象的垃圾收集。
PhantomRefrence(虛引用)
虛顧名思義就是沒有的意思,建立虛引用之後通過get方法傳回結果始終為null,通過源代碼你會發現,虛引用通向會把引用的對象寫進referent,隻是get方法傳回結果為null。先看一下和gc互動的過程在說一下他的作用。
1 不把referent設定為null,直接把heap中的new String("abc")對象設定為可結束的(finalizable).
2 與軟引用和弱引用不同,先把PhantomRefrence對象添加到它的ReferenceQueue中,然後在釋放虛可及的對象。
你會發現在收集heap中的new String("abc")對象之前,你就可以做一些其他的事情。通過以下代碼可以了解他的作用。
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;
public class Test {
public static boolean isRun = true;
| |
}
結果為:
class java.[email protected]
gc will collect:class [email protected]