每種編程語言都有本身操做內存中元素的方式,例如在 C 和 C++ 裏是經過指針,而在 Java 中則是經過「引用」。在 Java 中一切都被視爲了對象,可是咱們操做的標識符其實是對象的一個引用(reference)。html
從JDK 1.2版本開始,把對象的引用分爲4種級別,從而使程序能更加靈活地控制對象的生命週期。這4種引用的強度依次減弱:強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4 種。java
//建立一個引用,引用能夠獨立存在,並不必定須要與一個對象關聯 String s; //經過這個引用標識符指向某個對象,以後即可以經過這個引用來實現操做對象了。 s = new String("abc"); System.out.println(str.toString());
Java 中的垃圾回收機制在判斷是否回收某個對象的時候,都須要依據「引用」這個概念。
在不一樣垃圾回收算法中,對引用的判斷方式有所不一樣:算法
Java中默認聲明的就是強引用,只要強引用存在,垃圾回收器將永遠不會回收被引用的對象,哪怕內存不足時,JVM也只會直接拋出OutOfMemoryError,不會去回收。若是想中斷強引用與對象之間的聯繫,能夠顯示的將強引用賦值爲null,這樣一來,JVM就能夠適時的回收對象了。例如:編程
//只要obj還指向Object對象,Object對象就不會被回收 Object obj = new Object(); //將強引用賦值爲null後JVM能夠回收 obj = null;
軟引用是用來描述一些非必需但仍有用的對象。在內存足夠的時候,軟引用對象不會被回收,只有在內存不足時,GC則會回收軟引用對象,若是回收了軟引用對象以後仍然沒有足夠的內存,纔會拋出內存溢出異常。這種特性經常被用來實現緩存技術,好比網頁緩存,圖片緩存等。在 JDK1.2 以後,用java.lang.ref.SoftReference類來表示軟引用。數組
接下來作個測試,個人內存配置以下圖:緩存
軟引用:編程語言
public static void testSoftReference() { for (int i = 0; i < 5; i++) { //建立弱引用字節數組 byte[] buff = new byte[1024 * 1024 * 1000]; SoftReference<byte[]> sr = new SoftReference<byte[]>(buff); list.add(sr); } //主動通知垃圾回收,內存不足時回收弱引用 System.gc(); for(int i=0; i < list.size(); i++){ Object obj = ((SoftReference) list.get(i)).get(); System.out.println(obj); } }
測試結果:
null null null null [B@14ae5a5
打印結果老是隻有最後一個對象被保留,其餘的obj全都被置空回收了。這裏就說明了在內存不足的狀況下,軟引用將會被自動回收。即便有 byte[] buff 引用指向對象, 且 buff 是一個strong reference, 可是 SoftReference sr 指向的對象仍然被回收了,這是由於Java的編譯器發現了在以後的代碼中, buff 已經沒有被使用了, 因此自動進行了優化。測試
強引用:優化
public static void testSoftReference1() { //強引用 byte[] buff = null; for (int i = 0; i < 5; i++) { buff = new byte[1024 * 1024 * 1000]; SoftReference<byte[]> sr = new SoftReference<byte[]>(buff); list.add(sr); } //主動通知垃圾回收,內存不足時回收弱引用 System.gc(); for(int i=0; i < list.size(); i++){ Object obj = ((SoftReference) list.get(i)).get(); System.out.println(obj); } }
結果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.demo.ReferenceTest.testSoftReference1(ReferenceTest.java:45) at com.demo.ReferenceTest.main(ReferenceTest.java:20)
buff 會由於強引用的存在,而沒法被垃圾回收,從而拋出OOM的錯誤。this
弱引用的引用強度比軟引用要更弱一些,不管內存是否足夠,只要 JVM 開始進行垃圾回收,那些被弱引用關聯的對象都會被回收。在 JDK1.2 以後,用 java.lang.ref.WeakReference 來表示弱引用。
public static void testWeakReference() { for (int i = 0; i < 5; i++) { byte[] buff = new byte[1024 * 1024 * 1000]; WeakReference<byte[]> sr = new WeakReference<byte[]>(buff); list.add(sr); } //主動通知垃圾回收 System.gc(); for(int i=0; i < list.size(); i++){ Object obj = ((WeakReference) list.get(i)).get(); System.out.println(obj); } }
結果:
null null null null null
能夠發現全部被弱引用關聯的對象都被垃圾回收了。
弱引用與軟引用的區別在於:只具備弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。
(1)概念
「虛引用」顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之 關聯的引用隊列中。
public class PhantomReference<T> extends Reference<T> { /** * Returns this reference object's referent. Because the referent of a * phantom reference is always inaccessible, this method always returns * <code>null</code>. * * @return <code>null</code> */ public T get() { return null; } public PhantomReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); } }
(2)引用隊列(ReferenceQueue)
引用隊列能夠與軟引用、弱引用以及虛引用一塊兒配合使用,當垃圾回收器準備回收一個對象時,若是發現它還有引用,那麼就會在回收對象以前,把這個引用加入到與之關聯的引用隊列中去。程序能夠經過判斷引用隊列中是否已經加入了引用,來判斷被引用的對象是否將要被垃圾回收,這樣就能夠在對象被回收以前採起一些必要的措施。
與軟引用、弱引用不一樣,虛引用必須和引用隊列一塊兒使用。