垃圾回收算法詳解(引用計數/標記-清除/標記壓縮/複製算法)

開門見山,常見的GC(垃圾回收)算法有以下四種,本文重在解析GC算法,是爲講解垃圾回收器做知識鋪墊。至於分代回收將在其它文章中補充說明。

目錄:

1. 引用計數法(Java沒有采用)

2. 標記-清除法 (jvm老年代回收)

3. 標記-壓縮法 (jvm老年代回收)

4. 複製算法 (jvm新生代回收)

5. 幾種算法對比

至於新生代和老年代的說法會在本文第4-5點簡要介紹

1. 引用計數法(Java沒有采用)

每個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數爲0時可以回收。此方法簡單,但無法解決對象相互循環引用的問題,如下圖所示。

①當出現左邊綠色的情況時,假設外部對A的引用消除,此時因爲A引用計數從1減爲0,A將被清除。從而對B的引用也消除,B的計數減爲0,GC將正確回收對象{A, B}

②然而若出現右邊橙色狀況,假設外部對E的引用消除,外部對於對象集{C,D,E}不再有引用,但他們之間出現循環引用現象,計數始終保持爲1,導致{C,D,E}無法被回收。

2. 標記-清除法

(這部分是我在課程展示時做的ppt講的,在原ppt上有動畫和僞代碼對比演示,以及如何對掃描整個堆的標記-清除法作出優化,想要擴展的可以點擊鏈接下載)

標記清掃回收器ppt演示(含動畫動態展示) 

標記-清掃式垃圾回收器是一種直接的全面停頓算法。簡單的說,它們找出所有不可達的對象,並將它們放入空閒列表Free。

清掃過程將分爲標記階段和清掃階段。以下是結合僞代碼的詳細分析。

①首先把根集所引用的對象reached(可達標誌位)設爲1,這裏假設根集引用了o1和o4,並將其加入到未掃描集合Unscanned中。至於根集是一組必須活躍的引用,比如所有活躍棧幀裏指向GC堆裏對象的引用(即正在被調用的引用類型參數和局部變量等)。

    對於根集的更多詳情可查看引文:對於根集的理解

② 刪除未掃描集合中的某個對象o1,並找到o1所引用的對象o3和o8;(原書寫的刪除對象並找到引用,我認爲是先找到引用再刪除,可能有翻譯上的誤差)

  再將o3和o8的reached標誌位設爲1,並將o3和o8放入未掃描集合中;

(未掃描集合中的對象表示是可達的,它們都可以被直接或間接引用)

③和以上步驟類似,此時刪除未掃描集合中的引用對象如o4,找出o4所引用的對象,改變被引用對象的標誌位爲1並放入未掃描集合。重複以上操作,直到Unsanned集合爲空,同時所有直接或間接引用的對象的 reached 位都被標記爲1,進入清掃階段;

④清掃所有 reached 標誌位爲0的對象(沒有被直接或間接訪問過),被清掃對象放入Free集合中;

⑤將存活的對象 reached 標誌位歸還位0,進行下一輪標記-清掃工作。
 

清理效果如圖所示:

缺點:需要掃描整個堆區,時間開銷較大。感興趣的可以查看我上傳的ppt中,標記-清除法的優化版。

 

3. 標記-壓縮法

總體思想和標記-清除法類似

①標記階段,通過根節點標記所有可達(直接或間接可訪問)對象,和標記-清除法類似;

②清除階段,將上一輪存活對象壓縮到內存的一端,之後清理邊界。(如此一來可以減少內存碎片,避免分配大對象時空間不夠)

清理效果如圖所示!

 

4. 複製算法 

在堆區中,對象分爲新生代(年輕代)、老年代和永生代,而複製算法發生是發生在新生代的。新建的對象一般分配在新生代的Eden區,當Eden快滿時進行一次小型的垃圾回收。存活的對象會移動到 Survivor1區(以下簡稱S1)。

當再次發生 GC 時,S1區的存活對象將複製到先前閒置的S2區,同時存活對象壽命+1;以後每次發生GC,S1和S2區將交替的作爲存活對象的存放區和閒置區。並且如果存活對象的壽命達到某個閾值,它將被分配到老年代中。(注意在JDK8中已經沒有老年代的概念,使用的是metaspace的概念,感興趣請參考 jdk8 Metaspace 調優

爲什麼需要年輕代:

按照GC的運行機制,會回收掉已經死掉的對象,而對象一般都是在年輕代就會死去,所以年輕代比老年代需要更頻繁的GC清理,下面針對年輕代與老年代的回收機制有不同的講解

存在的問題:

空間浪費,需要整合標記。所以大對象一般不放在複製空間,直接進入老年代。

 

5. 幾種算法對比

複製算法與標記-壓縮法對比:

複製收集算法在對象存活率較高時就要執行較多的複製操作,效率將會變低。更關鍵的是,如果不想浪費50%的空間,就需要有額外的空間進行分配擔保,以應對被使用的內存中所有對象都100%存活的極端情況,所以複製算法僅僅應用在新生代,而老年代一般不能直接選用這種算法,使用標記-壓縮法。

標記-壓縮法與標記-清除法對比:

根據老年代的特點,有人提出了“標記-壓縮”(Mark-Compact)算法,標記過程仍然與“標記-清除”算法一樣,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。也可以減少內存碎片。

---------------------------------------------------------------------------------------------------------------------------------------------------

感謝您的觀看!

參考資料和圖片來源:

https://blog.csdn.net/qq_33048603/article/details/52727991

https://www.cnblogs.com/ityouknow/p/5614961.html

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