Java-GC-標記壓縮算法


標記壓縮算法

其分為兩個階段標記階段,和壓縮階段.其中標記階段和標記清除算法的標記階段是一樣的.

  • 對壓縮算法來說,他的工作就是移動所有的可達對象到堆內存的同一區域中,使它們緊湊的排列在一起,從而將所有非可達對象釋放出來的空閑內存集中在一起,以防出現標記清除算法的弊端.

在壓縮階段,由於要移動可達對象,那么就要考慮移動對象時候的順序問題,一般分為一下三種:

  1. 任意順序,不考慮原先對象的排列順序,也不考慮對象之間的引用關系,隨意移動可達對象,這樣可能會有內存訪問的局部性問題.
  2. 線性順序,在重新排列對象時,會考慮到對象之間的引用關系,例如對象A引用了對象V,那么就會盡量將對象A,B排列在一起.
  3. 滑動排序,按照原先的排列順序滑動到堆的另一端.
    現在大部分垃圾回收算法都是按照任意順序或者是滑動順序去實現的.

Two-Finger算法

其實質是任意順序移動,其最適合處理包含固定大小對象的內存區域.

該算法需要遍歷堆內存兩次,第一次是將堆尾的可達對象移動到堆開始的空閑內存單元(所以在前面說,最適合固定對象長度).第二次遍歷則需要修改可達對象的引用,因為一些可達對象已經被移動到別的地址,而原先引用它們的對象還指向之前的地址.在這兩次的遍歷過程中,首位兩個指針分別從對的頭尾兩個位置向中間移動,直至兩個指針相遇.

LISP2算法

lisp2算法是一種應用更加廣泛的壓縮算法,它屬於滑動順序算法的一種.他和二指算法的不同之處在於它還可以處理大小不同的對象,而不是固定大小的對象(因此適用范圍更加廣泛).同時,計算出來的可達對象的遷移地址需要額外的空間進行存儲而不是復寫原先對象所在的位置.最后Lisps2需要三次堆內存的遍歷.

第一次遍歷

collector僅僅計算和記錄可達對象應該遷移過去的地址.

compact():
    computeLocations(HeapStart,HeapEnd,HeapStart)
    updateReferences(HeapStart,HeapEnd)
    relocate(HeapStart,HeapEnd)
    
computeLocations(start,end,toRegion):
    scan <- start
    free <- toRegion
    while scan < end
        if isMarked(scan)
            forwardingAddress(scan) <- free
            free <- free + size(scan)
        scan <- scan + size(scan)

  1. 指針free和scan同時指向起始堆位置,同時scan開始向堆尾移動,目的是要找到被標記的可達對象.

  2. 發現可達對象之后,scan指針對應的位置分配一個額外的空間來存儲該可達對象應該遷移到的地址,就是free指針指向的位置0,同時free指針向堆尾移動B對象大小的距離,free指針指向的位置.最后指針繼續向前走,直到尋找到下一個可達對象D scan指針指向的位置.

  3. 同理,在可達對象D處分配一塊空間來保存對象D應該遷移到的位置,由於B對象已經占用了2個內存單元,所以對象E的遷移地址是從位置2開始,也就是當前free指針指向的位置。

  4. 指針free,scan繼續向前移動。

  5. 第一次遍歷完后,所有的可達對象都有了對應的遷移地址,free指針指向位置9,因為所有的可達對象總共占了9個單元大小的空間。

第二次遍歷

第二次遍歷主要是修改對象之間的引用關系,基本和二指算法的第二次遍歷一樣.

updateReferences(start,end):
    for each fld in Roots
        ref <- *fld
        if ref != null
            *fld <- forwardingAddress(ref)
    
    scan <- start
    while scan < end
        if isMarked(scan)
            for each fld in Pointers(scan)
                if *fld != null
                    *fld <- forwardingAddress(*fld)
        scan <- scan + size(scan)

  1. 修改根對象的引用關系,根對象1引用對象B,對象B的遷移地址為0,於是collector將根對象對B對象的引用指向它的遷移地址 - 位置0, 現在A對象所處的位置。

  2. 同理,對於根對象2,3都執行同樣的操作,將它們對其所引用的對象的引用修改為對應的它們所引用的對象的遷移地址。

  3. 通過scan指針遍歷堆內存,更新所有的可達對象對其引用對象的引用為其引用對象的遷移地址。比如說,對於可達對象B,它引用了對象D,D的遷移地址是2,那么B直接將其對D對象的引用重新指向2這個位置。

  4. 第二次遍歷結束后的對象之間的引用關系。

第三次遍歷

第三次遍歷則是根據可達對象的遷移地址去移動可達對象,比如說可達對象B,它的遷移地址是0,那么就將其移動到位置0,同時去除可達對象的標記,以便下次垃圾收集。

relocate(start,end):
    scan <- start
    while scan < end
        if isMarked(scan)
            dest <- forwardingAddress(scan)
            move(scan,dest) //將可達對象從scan位置移動到dest位置
            unsetMarked(dest)
        scan <- scan + size(scan)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM