簡介:
本文主要介紹JAVA中的四種引用: StrongReference(強引用)、SoftReferenc(軟引用)、WeakReferenc(弱引用)、PhantomReference(虛引用)的作用。同時我們還将介紹ReferenceQueue和WeakHashMap的功能和使用示例。
歡迎探讨,如有錯誤敬請指正
如需轉載,請注明出處 http://www.cnblogs.com/nullzx/
1. JAVA中的四種引用
四種引用中,軟引用、若引用、虛引用都需要相關類來建立。建立的時候都需要傳遞一個對象,然後通過引用的get方法擷取真正的對象。
1.1 StrongReference(強引用)
強引用就是我們一般在程式中引用一個對象的方式
Object obj = new Object();
obj就是一個強引用。垃圾回收器絕不會回收它,當記憶體空間不足,Java虛拟機甯願抛出OutOfMemoryError錯誤,使程式異常終止,也不會靠回收具有強引用的對象來解決記憶體不足的問題。
1.2 SoftReference(軟引用)
軟引用的建立要借助于java.lang.ref包下的SoftReferenc類。當JVM進行垃圾回收時,隻有在記憶體不足的時候JVM才會回收僅有軟引用指向的對象所占的空間。
package javalearning;
import java.lang.ref.SoftReference;
/*
* 虛拟機參數配置
* -Xms256m
* -Xmx1024m
*/
public class SoftReferenceDemo {
public static void main(String[] args){
/*軟引用對象中指向了一個長度為300000000個元素的整形數組*/
SoftReference<int[]> softReference =
new SoftReference<int[]>(new int[300000000]);
/*主動調用一次gc,由于此時JVM的記憶體夠用,此時softReference引用的對象未被回收*/
System.gc();
System.out.println(softReference.get());
/*消耗記憶體,會導緻一次自動的gc,此時JVM的記憶體不夠用
*就回收softReference對象中指向的數組對象*/
int[] strongReference = new int[100000000];
System.out.println(softReference.get());
}
}
我們應該注意到,上面的代碼中名為softReference的引用指向了一個
SoftReference對象,這個指向還是一個強引用類型。而SoftReference對象中指向int類型數組的引用就是一個軟引用類型了。
運作結果
[I@2a139a55
null
1.3 WeakReference(弱引用)
弱引用的建立要借助于java.lang.ref包下的WeakReferenc類。當JVM進行垃圾回收時,無論記憶體是否充足,都會回收僅被弱引用關聯的對象。由于垃圾回收器是一個優先級很低的線程,是以不一定會很快發現那些被弱引用指向的對象。
package javalearning;
import java.lang.ref.WeakReference;
public class WeakReferenceDemo {
public static void main(String[] args){
/*若引用對象中指向了一個長度為1000個元素的整形數組*/
WeakReference<String[]> weakReference =
new WeakReference<String[]>(new String[1000]);
/*未執行gc,目前僅被弱引用指向的對象還未被回收,是以結果不是null*/
System.out.println(weakReference.get());
/*執行一次gc,即使目前JVM的記憶體夠用,但還是回收僅被弱引用指向的對象*/
System.gc();
System.out.println(weakReference.get());
}
}
同理,上面的代碼中名為weakReference的引用指向了一個
WeakReference對象,這個指向還是一個強引用類型。而WeakReference對象中指向String類型數組的引用就是一個弱引用類型了。
[Ljava.lang.String;@2a139a55
null
1.4 PlantomReference(虛引用)
如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。建立一個虛引用對象時必須還要傳遞一個引用隊列(ReferenceQueue)。
2. ReferenceQueue(引用隊列)簡介
當gc(垃圾回收線程)準備回收一個對象時,如果發現它還僅有軟引用(或弱引用,或虛引用)指向它,就會在回收該對象之前,把這個軟引用(或弱引用,或虛引用)加入到與之關聯的引用隊列(ReferenceQueue)中。如果一個軟引用(或弱引用,或虛引用)對象本身在引用隊列中,就說明該引用對象所指向的對象被回收了。
當軟引用(或弱引用,或虛引用)對象所指向的對象被回收了,那麼這個引用對象本身就沒有價值了,如果程式中存在大量的這類對象(注意,我們建立的軟引用、弱引用、虛引用對象本身是個強引用,不會自動被gc回收),就會浪費記憶體。是以我們這就可以手動回收位于引用隊列中的引用對象本身。
除了上面代碼展示的建立引用對象的方式。軟、弱、虛引用的建立還有另一種方式,即在建立引用的同時關聯一個引用隊列。
SoftReference(T referent, ReferenceQueue<? super T> q)
WeakReference(T referent, ReferenceQueue<? super T> q)
PhantomReference(T referent, ReferenceQueue<? super T> q)
下面的示例中我們利用ReferenceQueue回收SoftReference對象本身。
package javalearning;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class ReferenceQueneDemo {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args){
/*建立引用隊列*/
ReferenceQueue<SoftReference<int[]>> rq =
new ReferenceQueue<SoftReference<int[]>>();
/*建立一個軟引用數組,每一個對象都是軟引用類型*/
SoftReference<int[]>[] srArr = new SoftReference[1000];
for(int i = 0; i < srArr.length; i++){
srArr[i] = new SoftReference(new int[300000], rq);
}
/*(可能)在gc前保留下了三個強引用*/
int[] arr1 = srArr[30].get();
int[] arr2 = srArr[60].get();
int[] arr3 = srArr[90].get();
/*占用記憶體,會導緻一次gc,使得隻有軟引用指向的對象被回收*/
int[] strongRef = new int[200000000];
Object x;
int n = 0;
while((x = rq.poll()) != null){
int idx = 0;
while(idx < srArr.length){
if(x == srArr[idx]){
System.out.println("free " + x);
srArr[idx] = null; /*手動釋放記憶體*/
n++;
break;
}
idx++;
}
}
/*當然最簡單的方法是通過isEnqueued()判斷一個軟引用方法是否在
* 隊列中,上面的方法隻是舉例
int n = 0;
for(int i = 0; i < srArr.length; i++){
if(srArr[i].isEnqueued()){
srArr[i] = null;
n++;
}
}
*/
System.out.println("recycle " + n + " SoftReference Object");
}
}
運作結果(省略部分結果)
……
……
……
free java.lang.ref.SoftReference@cc285f4
free java.lang.ref.SoftReference@55f3ddb1
free java.lang.ref.SoftReference@8bd1b6a
free java.lang.ref.SoftReference@18be83e4
free java.lang.ref.SoftReference@cb5822
recycle 997 SoftReference Object
從上面的例子中可以看出,我們回收SoftReference對象的效率并不高。原因是每從隊列中取出一個SoftReference引用,就是我們必須和SoftReference[]數組中的每一個對象逐個比較。這樣的查找方式顯然不及HashMap,是以我們自然想到建構一個引用類型的HashMap來解決這個問題。而實際上JDK中已經提供了一個具有這樣功能的類,即WeakHashMap。
3. WeakHashMap簡介
WeakHahsMap 的實作原理簡單來說就是HashMap裡面的條目 Entry繼承了 WeakReference,那麼當 Entry 的 key 不再被使用(即,引用對象不可達)且被 GC 後,那麼該 Entry 就會進入到 ReferenceQueue 中。當我們調用WeakHashMap 的get和put方法會有一個副作用,即清除無效key對應的Entry。這個過程就和上面的代碼很類似了,首先會從引用隊列中取出一個Entry對象,然後在HashMap中查找這個Entry對象的位置,最後把這個 Entry 從 HashMap中删除,這時key和value對象都被回收了。重複這個過程直到隊列為空。
最後說明一點,WeakHashMap是線程安全的。
package javalearning;
import java.util.WeakHashMap;
public class WeakHashMapDemo {
public static void main(String[] args){
WeakHashMap<String, byte[]> whm = new WeakHashMap<String, byte[]>();
String s1 = new String("s1");
String s2 = new String("s2");
String s3 = new String("s3");
whm.put(s1, new byte[100]);
whm.put(s2, new byte[100]);
whm.put(s3, new byte[100]);
s2 = null;
s3 = null;
/*此時可能還未執行gc,是以可能還可以通過僅有弱引用的key找到value*/
System.out.println(whm.get("s1"));
System.out.println(whm.get("s2"));
System.out.println(whm.get("s3"));
System.out.println("-------------------");
/*執行gc,導緻僅有弱引用的key對應的entry(包括value)全部被回收*/
System.gc();
System.out.println(whm.get("s1"));
System.out.println(whm.get("s2"));
System.out.println(whm.get("s3"));
}
}
[B@2a139a55
[B@15db9742
[B@6d06d69c
-------------------
[B@2a139a55
null
null
4. 參考内容
[1] Java中的強引用,軟引用,弱引用,虛引用有什麼用?
[2] ReferenceQueue的使用
[3] 強軟弱虛---強引用、軟引用、弱引用、虛引用