強引用是使用最廣泛的引用。若是一個對象具備強引用,那垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足的問題。 ps:強引用其實也就是咱們平時A a = new A()這個意思。java
<br/> ## Final Reference * 當前類是不是finalizer類,注意這裏finalizer是由JVM來標誌的( 後面簡稱f類 ),並非指java.lang.ref.Fianlizer類。可是f類是會被JVM註冊到java.lang.ref.Fianlizer類中的。緩存
① 當前類或父類中含有一個參數爲空,返回值爲void的名爲finalize的方法。
② 而且該finalize方法必須非空ide
詳見:JVM源碼分析之FinalReference徹底解讀 - 你假笨源碼分析
是用來描述一些還有用但並不是必須的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常以前,將會把這些對象列進回收範圍之中進行第二次回收。若是此次回收尚未足夠的內存,纔會拋出內存溢出異常。 對於軟引用關聯着的對象,若是內存充足,則垃圾回收器不會回收該對象,若是內存不夠了,就會回收這些對象的內存。在 JDK 1.2 以後,提供了 SoftReference 類來實現軟引用。軟引用可用來實現內存敏感的高速緩存。軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
注意:Java 垃圾回收器準備對SoftReference所指向的對象進行回收時,調用對象的 finalize() 方法以前,SoftReference對象自身會被加入到這個 ReferenceQueue 對象中,此時能夠經過 ReferenceQueue 的 poll() 方法取到它們。性能
/** * 軟引用:對於軟引用關聯着的對象,在系統將要發生內存溢出異常以前,將會把這些對象列進回收範圍之中進行第二次回收( 由於是在第一次回收後纔會發現內存依舊不充足,纔有了這第二次回收 )。若是此次回收尚未足夠的內存,纔會拋出內存溢出異常。 * 對於軟引用關聯着的對象,若是內存充足,則垃圾回收器不會回收該對象,若是內存不夠了,就會回收這些對象的內存。 * 經過debug發現,軟引用在pending狀態時,referent就已是null了。 * * 啓動參數:-Xmx5m * */ public class SoftReferenceDemo { private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>(); public static void main(String[] args) throws InterruptedException { Thread.sleep(3000); MyObject object = new MyObject(); SoftReference<MyObject> softRef = new SoftReference(object, queue); new Thread(new CheckRefQueue()).start(); object = null; System.gc(); System.out.println("After GC : Soft Get = " + softRef.get()); System.out.println("分配大塊內存"); /** * ====================== 控制檯打印 ====================== * After GC : Soft Get = I am MyObject. * 分配大塊內存 * MyObject's finalize called * Object for softReference is null * After new byte[] : Soft Get = null * ====================== 控制檯打印 ====================== * * 總共觸發了 3 次 full gc。第一次有System.gc();觸發;第二次在在分配new byte[5*1024*740]時觸發,而後發現內存不夠,因而將softRef列入回收返回,接着進行了第三次full gc。 */ // byte[] b = new byte[5*1024*740]; /** * ====================== 控制檯打印 ====================== * After GC : Soft Get = I am MyObject. * 分配大塊內存 * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space * at com.bayern.multi_thread.part5.SoftReferenceDemo.main(SoftReferenceDemo.java:21) * MyObject's finalize called * Object for softReference is null * ====================== 控制檯打印 ====================== * * 也是觸發了 3 次 full gc。第一次有System.gc();觸發;第二次在在分配new byte[5*1024*740]時觸發,而後發現內存不夠,因而將softRef列入回收返回,接着進行了第三次full gc。當第三次 full gc 後發現內存依舊不夠用於分配new byte[5*1024*740],則就拋出了OutOfMemoryError異常。 */ byte[] b = new byte[5*1024*790]; System.out.println("After new byte[] : Soft Get = " + softRef.get()); } public static class CheckRefQueue implements Runnable { Reference<MyObject> obj = null; [@Override](https://my.oschina.net/u/1162528) public void run() { try { obj = (Reference<MyObject>) queue.remove(); } catch (InterruptedException e) { e.printStackTrace(); } if (obj != null) { System.out.println("Object for softReference is " + obj.get()); } } } public static class MyObject { [@Override](https://my.oschina.net/u/1162528) protected void finalize() throws Throwable { System.out.println("MyObject's finalize called"); super.finalize(); } [@Override](https://my.oschina.net/u/1162528) public String toString() { return "I am MyObject."; } } }
用來描述非必須的對象,可是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生以前。當垃圾收集器工做時,不管當前內存是否足夠,都會回收掉只被弱引用關聯的對象。一旦一個弱引用對象被垃圾回收器回收,便會加入到一個註冊引用隊列中。
注意:Java 垃圾回收器準備對WeakReference所指向的對象進行回收時,調用對象的 finalize() 方法以前,WeakReference對象自身會被加入到這個 ReferenceQueue 對象中,此時能夠經過 ReferenceQueue 的 poll() 方法取到它們。ui
/** * 用來描述非必須的對象,可是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發送以前。當垃圾收集器工做時,不管當前內存是否足夠,都會回收掉只被弱引用關聯的對象。一旦一個弱引用對象被垃圾回收器回收,便會加入到一個註冊引用隊列中。 */ public class WeakReferenceDemo { private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>(); public static void main(String[] args) { MyObject object = new MyObject(); Reference<MyObject> weakRef = new WeakReference<>(object, queue); System.out.println("建立的弱引用爲 : " + weakRef); new Thread(new CheckRefQueue()).start(); object = null; System.out.println("Before GC: Weak Get = " + weakRef.get()); System.gc(); System.out.println("After GC: Weak Get = " + weakRef.get()); /** * ====================== 控制檯打印 ====================== * 建立的弱引用爲 : java.lang.ref.WeakReference@1d44bcfa * Before GC: Weak Get = I am MyObject * After GC: Weak Get = null * MyObject's finalize called * 刪除的弱引用爲 : java.lang.ref.WeakReference@1d44bcfa , 獲取到的弱引用的對象爲 : null * ====================== 控制檯打印 ====================== */ } public static class CheckRefQueue implements Runnable { Reference<MyObject> obj = null; @Override public void run() { try { obj = (Reference<MyObject>)queue.remove(); } catch (InterruptedException e) { e.printStackTrace(); } if(obj != null) { System.out.println("刪除的弱引用爲 : " + obj + " , 獲取到的弱引用的對象爲 : " + obj.get()); } } } public static class MyObject { @Override protected void finalize() throws Throwable { System.out.println("MyObject's finalize called"); super.finalize(); } @Override public String toString() { return "I am MyObject"; } } }
PhantomReference 是全部「弱引用」中最弱的引用類型。不一樣於軟引用和弱引用,虛引用沒法經過 get() 方法來取得目標對象的強引用從而使用目標對象,觀察源碼能夠發現 get() 被重寫爲永遠返回 null。
那虛引用到底有什麼做用?其實虛引用主要被用來 跟蹤對象被垃圾回收的狀態,經過查看引用隊列中是否包含對象所對應的虛引用來判斷它是否 即將被垃圾回收,從而採起行動。它並不被期待用來取得目標對象的引用,而目標對象被回收前,它的引用會被放入一個 ReferenceQueue 對象中,從而達到跟蹤對象垃圾回收的做用。
當phantomReference被放入隊列時,說明referent的finalize()方法已經調用,而且垃圾收集器準備回收它的內存了。
注意:PhantomReference 只有當 Java 垃圾回收器對其所指向的對象真正進行回收時,會將其加入到這個 ReferenceQueue 對象中,這樣就能夠追綜對象的銷燬狀況。這裏referent對象的finalize()方法已經調用過了。 因此具體用法和以前兩個有所不一樣,它必須傳入一個 ReferenceQueue 對象。當虛引用所引用對象準備被垃圾回收時,虛引用會被添加到這個隊列中。
Demo1:spa
/** * 虛引用也稱爲幽靈引用或者幻影引用,它是最弱的一種引用關係。一個持有虛引用的對象,和沒有引用幾乎是同樣的,隨時都有可能被垃圾回收器回收。 * 虛引用必須和引用隊列一塊兒使用,它的做用在於跟蹤垃圾回收過程。 * 當phantomReference被放入隊列時,說明referent的finalize()方法已經調用,而且垃圾收集器準備回收它的內存了。 */ public class PhantomReferenceDemo { private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>(); public static void main(String[] args) throws InterruptedException { MyObject object = new MyObject(); Reference<MyObject> phanRef = new PhantomReference<>(object, queue); System.out.println("建立的虛擬引用爲 : " + phanRef); new Thread(new CheckRefQueue()).start(); object = null; int i = 1; while (true) { System.out.println("第" + i++ + "次GC"); System.gc(); TimeUnit.SECONDS.sleep(1); } /** * ====================== 控制檯打印 ====================== * 建立的虛擬引用爲 : java.lang.ref.PhantomReference@1d44bcfa * 第1次GC * MyObject's finalize called * 第2次GC * 刪除的虛引用爲: java.lang.ref.PhantomReference@1d44bcfa , 獲取虛引用的對象 : null * ====================== 控制檯打印 ====================== * * 再通過一次GC以後,系統找到了垃圾對象,並調用finalize()方法回收內存,但沒有當即加入PhantomReference Queue中。由於MyObject對象重寫了finalize()方法,而且該方法是一個非空實現,因此這裏MyObject也是一個Final Reference。因此地刺GC完成的是Final Reference的事情。 * 第二次GC時,該對象真處理PhantomReference,此時,將PhantomReference加入虛引用隊列( PhantomReference Queue )。 * 並且每次gc之間須要停頓一些時間,已給JVM足夠的處理時間;若是這裏沒有TimeUnit.SECONDS.sleep(1); 可能須要gc到第五、6次纔會成功。 */ } public static class MyObject { @Override protected void finalize() throws Throwable { System.out.println("MyObject's finalize called"); super.finalize(); } @Override public String toString() { return "I am MyObject"; } } public static class CheckRefQueue implements Runnable { Reference<MyObject> obj = null; @Override public void run() { try { obj = (Reference<MyObject>)queue.remove(); System.out.println("刪除的虛引用爲: " + obj + " , 獲取虛引用的對象 : " + obj.get()); System.exit(0); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Q:👆瞭解下System.gc()操做,若是連續調用,若前一次沒完成,後一次可能會失效,因此鏈接調用System.gc()其實做用不大?
A:關於上面例子的問題咱們要補充兩點
① 首先咱們先來看下System.gc()的doc文檔:.net
/** * Runs the garbage collector. * <p> * Calling the <code>gc</code> method suggests that the Java Virtual * Machine expend effort toward recycling unused objects in order to * make the memory they currently occupy available for quick reuse. * When control returns from the method call, the Java Virtual * Machine has made a best effort to reclaim space from all discarded * objects. * <p> * The call <code>System.gc()</code> is effectively equivalent to the * call: * <blockquote><pre> * Runtime.getRuntime().gc() * </pre></blockquote> * * @see java.lang.Runtime#gc() */ public static void gc() { Runtime.getRuntime().gc(); }
當這個方法返回的時候,Java虛擬機已經盡最大努力去回收全部丟棄對象的空間了。
所以不存在這System.gc()操做連續調用時,若前一次沒完成,後一次可能會失效的狀況。以及「因此鏈接調用System.gc()其實做用不大」這個說法不對,應該說連續調用System.gc()對性能可定是有影響的,但做用之一就是能夠清除「漂浮垃圾」。
② 同時須要特別注意的是對於已經沒有地方引用的這些f對象,並不會在最近的那一次gc裏立刻回收掉,而是會延遲到下一個或者下幾個gc時才被回收,由於執行finalize方法的動做沒法在gc過程當中執行,萬一finalize方法執行很長呢,因此只能在這個gc週期裏將這個垃圾對象從新標活,直到執行完finalize方法將Final Reference從queue裏刪除,這樣下次gc的時候就真的是漂浮垃圾了會被回收。線程
Demo2:debug
public class PhantomReferenceDemo2 { public static void main(String[] args) { ReferenceQueue<MyObject> queue = new ReferenceQueue<>(); MyObject object = new MyObject(); Reference<MyObject> phanRef = new PhantomReference<>(object, queue); System.out.println("建立的虛擬引用爲 : " + phanRef); object = null; System.out.println(phanRef.get()); System.gc(); System.out.println("referent : " + phanRef); System.out.println(queue.poll() == phanRef); //true /** * ====================== 控制檯打印 ====================== * 建立的虛擬引用爲 : java.lang.ref.PhantomReference@1d44bcfa * null * referent : java.lang.ref.PhantomReference@1d44bcfa * true * ====================== 控制檯打印 ====================== * * 這裏由於MyObject沒有重寫finalize()方法,因此這裏的在System.gc()後就會處理PhantomReference加入到PhantomReference Queue中。 */ } public static class MyObject { @Override public String toString() { return "I am MyObject"; } } }