四種引用型別:強引用、軟引用、弱引用、虛引用
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