Java中的四種引用以及ReferenceQueue和WeakHashMap的使用示例

簡介html

本文主要介紹JAVA中的四種引用: StrongReference(強引用)、SoftReferenc(軟引用)、WeakReferenc(弱引用)、PhantomReference(虛引用)的做用。同時咱們還將介紹ReferenceQueue和WeakHashMap的功能和使用示例。java

 

歡迎探討,若有錯誤敬請指正 數組

如需轉載,請註明出處 http://www.cnblogs.com/nullzx/安全

 


1. JAVA中的四種引用

四種引用中,軟引用、若引用、虛引用都須要相關類來建立。建立的時候都須要傳遞一個對象,而後經過引用的get方法獲取真正的對象。spa

 

1.1 StrongReference(強引用).net

強引用就是咱們通常在程序中引用一個對象的方式線程

Object obj = new Object();3d

obj就是一個強引用。垃圾回收器毫不會回收它,當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠回收具備強引用的對象來解決內存不足的問題。htm

 

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] 強軟弱虛---強引用、軟引用、弱引用、虛引用

相關文章
相關標籤/搜索