1. 程式人生 > >四種引用型別:強引用、軟引用、弱引用、虛引用

四種引用型別:強引用、軟引用、弱引用、虛引用

java中除了基本資料型別的變數(int、long等),剩下的都是引用型別的變數,一共有四種不同的引用型別。

一、強引用(Strong Reference)

強引用就是最常見的對某個物件的引用,如下程式碼變數o就是對所建立的Object物件的一個強引用。

Object o = new Object();

存在強引用的物件,不會被垃圾回收,即便發生了OutOfMemoryError,我們來看如下的測試程式碼:

/**
 * 這是一個大物件
 * @author 白吃的午餐
 *
 */
public class BigObject {
	private String name;
	private int[] a = new int[1024];

	public BigObject(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "BigObject [name=" + name + "]";
	}
}

public class StrongReferenceTest {

	public static void main(String[] args) {
		List<BigObject> bigs = new LinkedList<BigObject>();
		
		for(int i=0; i<1000; i++) {
			BigObject bo = new BigObject("BigObject_" + i);
			bigs.add(bo);
		}
	}

}

輸出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space     at com.lizhihong.reference.BigObject.<init>(BigObject.java:5)     at com.lizhihong.reference.StrongReferenceTest.main(StrongReferenceTest.java:12)

二、軟引用(Soft Reference)

軟引用是強度僅次於強引用的一種引用型別,當JVM認為記憶體不足時,會回收軟引用所指向的物件

public class SoftReferenceTest {

	public static void main(String[] args) throws InterruptedException {
		List<Reference<BigObject>> bigs = new LinkedList<Reference<BigObject>>();
		
		ReferenceQueue<BigObject> rq = new ReferenceQueue<BigObject>();
		BigObject bo = new BigObject("BigObject");
		SoftReference<BigObject> sr = new SoftReference<BigObject>(bo, rq);
		bo = null;
		
		System.gc();
		//記憶體充足,物件不會被回收,仍然可以被獲取到
		System.out.println(sr.get());
		
		//記憶體不足時,會回收軟應用指向的物件,不會丟擲OutOfMemoryError
		for(int i=0; i<1000; i++) {
			BigObject bo2 = new BigObject("BigObject_" + i);
			SoftReference<BigObject> sr2 = new SoftReference<BigObject>(bo2);
			bigs.add(sr2);
			bo = null;
		}
		
		//物件已經被回收,返回null
		System.out.println(sr.get());
	}
}

輸出:

BigObject [name=BigObject] null

三、弱引用(Weak Reference)

弱引用是強度次於強引用和軟引用的一種引用型別,JVM每次GC時,都有可能回收弱引用指向的物件

public class WeakReferenceTest {

	public static void main(String[] args) {
		BigObject bo = new BigObject("BigObject_0");
		WeakReference<BigObject> wr = new WeakReference<BigObject>(bo);
		bo = null;
		System.gc();
		//與軟引用不同,jvm每次gc時都有可能回收弱引用指向的物件,此處輸出為null
		System.out.println(wr.get());
	}

}

輸出

null

四、虛引用(Phantom Reference)

虛引用的概念比較難理解,你不能通過一個虛引用訪問它指向的物件,PhantomReference的get方法永遠返回null,僅僅是提供了物件被實際回收前做某些事情的機制,有點類似於finalize方法,但這個機制其實發生在finalize之後。

首先我們來澄清一個說法:虛引用是強度小於弱引用的一種引用型別

這個說法我個人覺得不完全正確,我們來看下面的這段程式碼會輸出什麼。如果虛引用是強度更弱的一種引用型別,下面這段程式碼應該不會報錯,因為GC會正常回收虛引用指向的物件,就像軟引用和弱引用一樣,但實際執行的情況呢

public class PhantomReferenceTest1 {

	public static void main(String[] args) {
		List<Reference<BigObject>> bigs = new LinkedList<Reference<BigObject>>();
		ReferenceQueue<BigObject> rq = new ReferenceQueue<BigObject>();
		
		for(int i=0; i<1000; i++) {
			BigObject bo = new BigObject("BigObject_" + i);
			PhantomReference<BigObject> pr = new PhantomReference<BigObject>(bo, rq);
			bigs.add(pr);
			bo=null;
		}
	}
	
}

輸出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space     at com.lizhihong.reference.BigObject.<init>(BigObject.java:10)     at com.lizhihong.reference.PhantomReferenceTest1.main(PhantomReferenceTest1.java:16)

OutOfMemoryError,說明虛引用指向的物件並沒有被回收,這是為什麼?

下面來說說我的理解,如有錯誤的地方,還請大家指教

1、以弱引用型別舉例,當我們建立一個弱引用的時候,最終會呼叫Reference如下的建構函式

private T referent;         /* Treated specially by GC */
    
Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }

referent,分明就是一個強引用,它所指向的物件為什麼會被回收呢

2、當JVM執行GC,回收弱引用所指向的物件時,實際上JVM會先把referent置為null,這樣此前referent指向的物件就沒有任何引用了,可以被JVM回收,實際情況正式這樣的,我們來看下WeakReference的java doc說明

/**  * Weak reference objects, which do not prevent their referents from being  * made finalizable, finalized, and then reclaimed.  Weak references are most  * often used to implement canonicalizing mappings.  *  * <p> Suppose that the garbage collector determines at a certain point in time  * that an object is <a href="package-summary.html#reachability">weakly  * reachable</a>.  At that time it will atomically clear all weak references to  * that object and all weak references to any other weakly-reachable objects  * from which that object is reachable through a chain of strong and soft  * references.  At the same time it will declare all of the formerly  * weakly-reachable objects to be finalizable.  At the same time or at some  * later time it will enqueue those newly-cleared weak references that are  * registered with reference queues.  *  * @author   Mark Reinhold  * @since    1.2  */

3、為什麼虛引用指向的物件沒有被回收,同樣我們檢視PhantomReference的java doc說明

/**  * Phantom reference objects, which are enqueued after the collector  * determines that their referents may otherwise be reclaimed.  Phantom  * references are most often used for scheduling pre-mortem cleanup actions in  * a more flexible way than is possible with the Java finalization mechanism.  *  * <p> If the garbage collector determines at a certain point in time that the  * referent of a phantom reference is <a  * href="package-summary.html#reachability">phantom reachable</a>, then at that  * time or at some later time it will enqueue the reference.  *  * <p> In order to ensure that a reclaimable object remains so, the referent of  * a phantom reference may not be retrieved: The <code>get</code> method of a  * phantom reference always returns <code>null</code>.  *  * <p> Unlike soft and weak references, phantom references are not  * automatically cleared by the garbage collector as they are enqueued.  An  * object that is reachable via phantom references will remain so until all  * such references are cleared or themselves become unreachable.  *  * @author   Mark Reinhold  * @since    1.2  */

4、虛引用指向的物件什麼時候被回收,這取決於JVM