垃圾回收--JAVA成長之路

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}無法被回收。

垃圾回收--JAVA成長之路

2. 標記-清除法

(這部分是我在課程展示時做的ppt講的,在原ppt上有動畫和虛擬碼對比演示,以及如何對掃描整個堆的標記-清除法作出最佳化,想要擴充套件的可以點選連結下載)

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

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

清掃過程將分為標記階段和清掃階段。以下是結合虛擬碼的詳細分析。

①首先把根集所引用的物件reached(可達標誌位)設為1,這裡假設根集引用了o1和o4,並將其加入到未掃描集合Unscanned中。至於根集是一組必須活躍的引用,比如所有活躍棧幀裡指向GC堆裡物件的引用(即正在被呼叫的引用型別引數和區域性變數等)。

對於根集的更多詳情可檢視引文:對於根集的理解

垃圾回收--JAVA成長之路

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

垃圾回收--JAVA成長之路

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

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

垃圾回收--JAVA成長之路

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

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

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

垃圾回收--JAVA成長之路

清理效果如圖所示:

垃圾回收--JAVA成長之路

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

3. 標記-壓縮法

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

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

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

清理效果如圖所示!

垃圾回收--JAVA成長之路

4. 複製演算法

在堆區中,物件分為新生代(年輕代)、老年代和永生代,而

複製演算法發生是發生在新生代的

。新建的物件一般分配在新生代的Eden區,當Eden快滿時進行一次小型的垃圾回收。存活的物件會移動到 Survivor1區(以下簡稱S1)。

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

垃圾回收--JAVA成長之路

為什麼需要年輕代:

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

存在的問題:

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

5. 幾種演算法對比

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

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

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

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

垃圾回收--JAVA成長之路