1.背景
想要理解物件什么時候回收,就要理解到物件參考這個概念,于是有了下文
2.java中參考物件結構圖

3.參考詳解
3.1.什么是強參考
a.當記憶體不足,JVM開始垃圾回收,對于強參考的物件,就算是出現了00M也不會對該物件進行回收,死都不收,
b.強參考是我們最常見的普通物件參考,只要還有強參考指向一個物件,就能表明物件還“活著”,垃圾收集器不會碰這種物件,
在Java中最常見的就是強參考,把一個物件賦給一個參考變數,這個參考變數就是一個強參考,
當一個物件被強參考變數參考時,它處于可達狀態,它是不可能被垃圾回識訓制回收的,即使該物件以后永遠都不會被用到JVM也不會回收,
因此強參考是造成Java記憶體泄漏的主要原因之一
c.對于一個普通的物件,如果沒有其他的參考關系,只要超過了參考的作用域或者顯式地將相應(強)參考賦值為null,一般認為就是可以被垃圾收集的了〈當然具體回收時機還是要看垃圾收集策略),
案例:
package com.wfd360.demo03GC.referDemo; /** * @author 姿勢帝-博客園 * @address https://www.cnblogs.com/newAndHui/ * @WeChat 851298348 * @create 06/20 12:12 * @description */ public class StrongRefer { /** * 強參考的理解 * * @param args */ public static void main(String[] args) { Object obj1 = new Object(); // 建立強參考 Object obj2 = obj1; // 觀察obj1 和 obj2 的各種記憶體地址 System.out.println("obj1=" + obj1); System.out.println("obj2=" + obj2); // obj1創建可以回收的條件 obj1 = null; // gc回收 System.gc(); // 觀察各物件情況 System.out.println("obj1=" + obj1); System.out.println("obj2=" + obj2); } }View Code

從測驗結果課程看出,obj1的實際物件別沒有回收;
3.2.什么是軟參考
a.軟參考是用來描述一些還有用但并非必需的物件,需要用java.lang.ref.SoftReference類來實作,
b.對于軟參考關聯著的物件,在系統將要發生記憶體溢位例外之前,將會把這些物件列進回收范圍之中進行第二次回收,如果這次回識訓沒有足夠的記憶體,才會拋出記憶體溢位例外,在JDK1.2之后,提供了Soft Reference類來實作軟參考,
c.軟參考通常用在對記憶體敏感的程式中,比如高速快取就有用到軟參考,記憶體夠用的時候就保留,不夠用就回收!
案例:
package com.wfd360.demo03GC.referDemo; import java.lang.ref.SoftReference; /** * @author 姿勢帝-博客園 * @address https://www.cnblogs.com/newAndHui/ * @WeChat 851298348 * @create 06/20 12:12 * @description */ public class SoftRefer { /** * 軟參考的理解 * 通過設定jvm引數,在不同的條件下觀察 * * @param -Xms5m -Xmx5m -XX:+PrintGCDetails * @param args */ public static void main(String[] args) { // 測驗記憶體充足(不回收軟參考) //testSoftReferNOGc(); // 測驗記憶體不充足(回收軟參考) testSoftReferGc(); } /** * 模擬記憶體充足的情況 */ public static void testSoftReferNOGc() { Object obj1 = new Object(); // 建立軟參考 SoftReference softRefer = new SoftReference<>(obj1); // 觀察記憶體地址 System.out.println("obj1=" + obj1); System.out.println("softRefer=" + softRefer.get()); // obj1創建可以回收的條件 obj1 = null; // gc回收 System.gc(); // 再次觀察記憶體地址 System.out.println("obj1=" + obj1); System.out.println("softRefer=" + softRefer.get()); } /** * 模擬記憶體不足 * 1.設定較小的堆記憶體 * 2.創建大物件 * 3.jvm參 * -Xms5m -Xmx5m -XX:+PrintGCDetails */ public static void testSoftReferGc() { Object obj1 = new Object(); // 建立軟參考 SoftReference softRefer = new SoftReference<>(obj1); // 觀察記憶體地址 System.out.println("obj1=" + obj1); System.out.println("softRefer=" + softRefer.get()); // obj1創建可以回收的條件 obj1 = null; try { byte[] bytes = new byte[6 * 1024 * 1024]; } catch (Throwable e) { System.out.println("===============>error:" + e.getMessage()); } finally { // 再次觀察記憶體地址 System.out.println("obj1=" + obj1); System.out.println("softRefer=" + softRefer.get()); } } }View Code
記憶體充足測驗結果:

記憶體不充足測驗結果:

實際案例
假如有一個應用需要讀取大量的本地資料(圖片、通訊率、臨時檔案等):
如果每次讀取資料都從硬碟讀取則會嚴重影響性能,
如果一次性全部加載到記憶體中又可能造成記憶體溢位,
此時使用軟參考可以解決這個問題,
設計思路是:用一個HashMap來保存資料的路徑和相應資料物件關聯的軟參考之間的映射關系,在記憶體不足時,
JVM會自動回收這些快取資料物件所占用的空間,從而有效地避免了00M的問題,
Map<String,SoftReference>imageCache=new HashMap<String,SoftReference>();
3.3.什么是弱參考
a.弱參考也是用來描述非必需物件的,但是它的強度比軟參考更弱一些,被弱參考關聯的物件只能生存到下一次垃圾收集發生之前,
b..當垃圾收集器作業時,無論當前記憶體是否足夠,都會回收掉只被弱參考關聯的物件,在JDK1.2之后,提供廣Weak Reference類來實作弱參考,
c.弱參考需要用Java.lang.ref.WeakReference類來實作,它比軟參考的生存期更短.
案例:
package com.wfd360.demo03GC.referDemo; import java.lang.ref.WeakReference; /** * @author 姿勢帝-博客園 * @address https://www.cnblogs.com/newAndHui/ * @WeChat 851298348 * @create 06/20 12:12 * @description */ public class WeakRefer { /** * 弱參考的理解 * * @param args */ public static void main(String[] args) { Object obj1 = new Object(); // 建立弱參考 WeakReference softRefer = new WeakReference<>(obj1); // 觀察記憶體地址 System.out.println("obj1=" + obj1); System.out.println("softRefer=" + softRefer.get()); // obj1創建可以回收的條件 obj1 = null; // gc回收 System.gc(); // 再次觀察記憶體地址 System.out.println("obj1=" + obj1); System.out.println("softRefer=" + softRefer.get()); } }View Code

擴展知識-WeakHashMap
查看API介紹:

測驗代碼:
package com.wfd360.demo03GC.referDemo; import java.util.HashMap; import java.util.WeakHashMap; /** * @author 姿勢帝-博客園 * @address https://www.cnblogs.com/newAndHui/ * @WeChat 851298348 * @create 06/20 5:10 * @description <p> * 弱參考參考之:WeakHashMap * 以弱鍵 實作的基于哈希表的 Map,在 WeakHashMap 中,當某個鍵不再正常使用時,將自動移除其條目, * 更精確地說,對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止, * 然后被回收,丟棄某個鍵時,其條目從映射中有效地移除,因此,該類的行為與其他的 Map 實作有所不同, * </p> */ public class WeakReferMap { /** * 測驗 HashMap 與 WeakHashMap 區別 * 測驗邏輯: * 1.創建不同的map * 2.創建key value值 * 3.放入各自的map,并列印結果 * 4.將key設定為null,并列印結果 * 5.手動GC,并列印結果 * * @param args */ public static void main(String[] args) { hashMapMethod(); System.out.println("--------華麗的分割線--------"); weakHashMapMethod(); } /** * HashMap測驗(強參考) */ private static void hashMapMethod() { HashMap<String, String> map = new HashMap<>(); String key = "key1"; String value = "HashMap-value"; map.put(key, value); System.out.println(map); key = null; System.out.println(map); System.gc(); System.out.println(map); } /** * 若參考(WeakHashMap測驗) */ private static void weakHashMapMethod() { WeakHashMap<String, String> map = new WeakHashMap<>(); // 注意這里的new一個字串與直接寫key="key2"對測驗結果是有區別的,詳細原因可以看之前講的記憶體分配 String key = new String("key2"); String value = "WeakHashMap-value"; map.put(key, value); System.out.println(map); key = null; System.out.println(map); System.gc(); System.out.println(map); } }View Code
測驗結果:

從測驗結果可以看出:弱引用的map資料已經被回收,
擴展知識-ReferenceQueue參考佇列

代碼:
package com.wfd360.demo03GC.referDemo; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; /** * @author 姿勢帝-博客園 * @address https://www.cnblogs.com/newAndHui/ * @WeChat 851298348 * @create 06/20 7:23 * @description */ public class QueueRefer { /** * 測驗弱參考回收前,把資料放入佇列中 * * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Object obj1 = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue(); // 當GC釋放物件記憶體的時候,會將參考加入到參考佇列 WeakReference<Object> weakReference = new WeakReference<>(obj1, referenceQueue); System.out.println(obj1); System.out.println(weakReference.get()); System.out.println(referenceQueue.poll()); System.out.println("--------華麗的分割線--------"); obj1 = null; System.gc(); Thread.sleep(500); System.out.println(obj1); System.out.println(weakReference.get()); System.out.println(referenceQueue.poll()); } }View Code
采用弱參考的方式測驗結果:

從測驗結果可以看出,需要回收的物件已經進入佇列,
采用軟參考的方式測驗結果:

從測驗結果可以看出,軟參考,沒有到達回收的條件,并沒有進行回收,也不會進入佇列;
3.4.什么是虛參考
1.虛參考需要java.lang.ref.PhantomReference類來實作,
2.與其他幾種參考都不同,虛參考并不會決定物件的生命周期,如果一個物件僅持有
虛參考,那么它就和沒有任何參考一樣,在任何時候都可能被垃圾回收器回收,它不能單獨使用也不能通過它訪
問物件,虛參考必須和參考佇列(ReferenceQueue)聯合使用,
3.虛參考的主要作用是跟蹤物件被垃圾回收的狀態,僅僅是提供了一種確保物件被finalize以后,做某些事情的
機制,PhantomReference的get方法總是回傳null,因此無法訪問對應的參考物件,其意義在于說明一個物件己
經進入俑finalization階段,可以被gc回收,用來實作比finalization機制更靈活的回收操作,
4.設定虛參考關聯的唯一目的,就是在這個物件被收集器回收的時候收到一個系統通知或者后續添加
進一步的處理,Java技術允許使用finalize()方法在垃圾收集器將物件從記憶體中清除出去之前做必要的清理作業,
代碼:
package com.wfd360.demo03GC.referDemo; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; /** * @author 姿勢帝-博客園 * @address https://www.cnblogs.com/newAndHui/ * @WeChat 851298348 * @create 06/20 7:44 * @description */ public class PhantomRefer { /** * 虛參考測驗 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Object obj1 = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue(); PhantomReference<Object> phantomReference = new PhantomReference<>(obj1,referenceQueue); System.out.println(obj1); System.out.println(phantomReference.get()); System.out.println(referenceQueue.poll()); System.out.println("--------華麗的分割線--------"); obj1 = null; System.gc(); Thread.sleep(500); System.out.println(obj1); System.out.println(phantomReference.get()); System.out.println(referenceQueue.poll()); } }View Code
測驗結果:

4.重要總結
物件是否存活判斷流程:
1.可達性分析,看是否有GC Roots的參考鏈,如果沒有將做第一次標記;
2.檢查是否需要執行finalize()方法,
如果沒必要(之前執行過了),直接回收記憶體;
如果要執行finalize()方法,這個時候物件如果再次建立參考鏈(唯一自救機會),物件不會被回收,否則直接回收;
總結:
1.物件回收滿足兩個條件:
a.沒有參考鏈,
b.回收前會執行finalize()方法,如果執行finalize(),沒有再次建立連接(如果重新與參考鏈上的任意物件建立連接,例如給物件賦值,該物件都不會被回收)
2.在gc回收前會執行finalize()方法,只執行一次,并且是異步執行不保證執行成功,執行緒優先級低
代碼演示:
package com.wfd360.demo03GC.referDemo; /** * @author 姿勢帝-博客園 * @address https://www.cnblogs.com/newAndHui/ * @WeChat 851298348 * @create 06/20 8:34 * @description */ public class FinalizeGC { public static FinalizeGC obj1 = null; /** * 重寫finalize方法 * @throws Throwable */ @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("執行finalize方法"); // 自救,在回收時建立參考鏈 FinalizeGC.obj1 = this; } public static void main(String[] args) throws InterruptedException { obj1 = new FinalizeGC(); obj1 = null; System.gc(); Thread.sleep(600); System.out.println("第一次自救成功:"+obj1); obj1 = null; System.gc(); Thread.sleep(600); System.out.println("第二次自救失敗,不會再次執行finalize方法:"+obj1); } }View Code
測驗結果:

完美!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/159458.html
標籤:Java
上一篇:使用SpringCloud Stream結合rabbitMQ實作訊息消費失敗重發機制
下一篇:Tomcat--配置
