【JVM】自動內存管理機制《三》---對象的生死判定和算法詳解

目錄

導讀

 對象存活判定標準

對象存活判定標準:

對象回收標準:

 對象存活判定算法

引用計數器算法

循環引用的問題

運行的結果:

可達性分析算法

小結


導讀

 上篇博客我們已經講了Java內存運行時區域的各個部分,其中程序計數器、虛擬機棧、本地方法棧3個區域是線程私有,也就是生死隨着線程。接下來本篇博客着重講:如何自動管理內存,今天解決第三個問題,第三個問題有些複雜,詳細會按以下思路講解-:如何自動管理的?誰管理的?答案是:對象的生死判定和回收垃圾收集器來管理的

接下來按下面的順序講述:

 內存回收機制

     *  對象存活判定算法

     * 垃圾收集算法

     *  垃圾收集器(對垃圾收集算法的實現)

 內存分配與回收策略

     * 原則

 對象存活判定標準

如果要講對象存活判定算法,首先要講的是:對象是否存活的標準,那麼算法就很簡單,對象存活判定算法就是對這個標準的代碼實現。但大家要區分存活標準和回收標準的區別,並不是沒死就不會被回收,就像你生活中一個物品不經常用,或者很少用,那麼你就可能會選擇丟掉啊。或者你覺得這個物品帶給你的價值已經大於他的某些弊端,那麼你就會丟掉。

對象存活判定標準:

一個對象“死去”,就以爲着不可能再被任何途徑使用的對象。無任何引用!


我們已經知道在jvm中,一個對象是否存活,只需要看是否被引用。什麼是引用:如果reference類型的數據中存儲的數值代表的是另一塊內存的起始地址,那麼就稱爲這塊內存代表一個引用。這種定義很純粹,但太過狹隘,只有被引用或者沒被引用兩種狀態。

爲什麼說它狹隘?生活中的而也是,當一個物品對我們來說可有可無,如果家裏有地方放那就留下,實在無可奈何纔會把它仍掉,這個時候用垃圾和不是垃圾兩個狀態就有些狹隘啦!所以我們如何描述一些“食之無味,棄之可惜”的對象呢??所以再jdk1.2之後,Java對引用的概念進行了擴充,分爲:強引用(Strong Reference).軟引用(Soft Reference),弱引用(Phantom Reference),虛引用(Phantom Reference),4種,引用強度依次減弱。代碼實踐對比大家可以參考這篇博客:JAVA四種引用方式對比總結 附測試demo

對象回收標準:

 對象存活判定算法

 上面說了對象存活判定的標準,那麼對象存活判定算法就是對標準的代碼實現


引用計數器算法

引用計算器判斷對象是否存活的算法是這樣的:給每一個對象設置一個引用計數器,每當有一個地方引用這個對象的時候,計數器就加1,與之相反,每當引用失效的時候就減1。

優點:實現簡單、性能高。

缺點:增減處理頻繁消耗cpu計算、計數器佔用很多位浪費空間、最重要的缺點是無法解決循環引用的問題

因爲引用計數器算法很難解決循環引用的問題,所以主流的Java虛擬機都沒有使用引用計數器算法來管理內存。

循環引用的問題

public class ReferenceDemo {
    public Object instance = null;
    private static final int _1Mb = 1024 * 1024;
    private byte[] bigSize = new byte[10 * _1Mb]; // 申請內存
    public static void main(String[] args) {
        System.out.println(String.format("開始:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));
        ReferenceDemo referenceDemo = new ReferenceDemo();
        ReferenceDemo referenceDemo2 = new ReferenceDemo();
        referenceDemo.instance = referenceDemo2;
        referenceDemo2.instance = referenceDemo;
        System.out.println(String.format("運行:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));
        referenceDemo = null;
        referenceDemo2 = null;
        System.gc(); // 手動觸發垃圾回收
        System.out.println(String.format("結束:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));
    }
}

運行的結果:

開始:178 M
運行:157 M
結束:181 M

從結果可以看出,虛擬機並沒有因爲相互引用就不回收它們,也側面說明了虛擬機並不是使用引用計數器實現的。

可達性分析算法

在主流的語言的主流實現中,比如Java、C#、甚至是古老的Lisp都是使用的可達性分析算法來判斷對象是否存活的。

這個算法的核心思路就是通過一些列的“GC Roots”對象作爲起始點,從這些對象開始往下搜索,搜索所經過的路徑稱之爲“引用鏈”。當一個對象到GC Roots沒有任何引用鏈相連的時候,證明此對象是可以被回收的。如下圖所示:

 

在Java中,哪些對象可作爲GC Roots對象:

  • Java虛擬機棧中的引用對象。
  • 本地方法棧中JNI(既一般說的Native方法)引用的對象。
  • 方法區中類靜態常量的引用對象。
  • 方法區中常量的引用對象。

小結

 到這裏我們已經知道啦對象存在哪?【JVM】自動內存管理機制《一》---內存劃分及異常可能情況;內存區域爲何劃分?【JVM】自動內存管理機制《二》---- 內存區域爲何劃分,以什麼原則劃分,爲何自動管理?;如何判定是否是垃圾,何時回收?【JVM】自動內存管理機制《三》---對象的生死判定和算法詳解;接下來就是誰來回收?就是接下來要講的垃圾收集算法,和垃圾收集器?【JVM】自動內存管理機制《三》---垃圾收集器(索命黑白無常回收垃圾對象),小手手點個讚唄~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章