GC算法-標記壓縮算法


 

概述

還記得標記清除復制算法的問題么? 堆使用效率低和碎片化問題. 那么有沒有能夠利用整個堆, 有沒有內存碎片化問題的算法呢? 這就是標記壓縮算法了.

簡單來說, 標記壓縮算法就是將堆中的所有活動對象整體向左移, 將對象間的空隙消除.

在GC執行前的內存:

GC執行后的內存:

恩, 就是這么個意思.

實現

如何實現上面的操作呢? 首先, 要將所有活動對象標記出來. 這是標記階段, 跳過了, 跟標記清除一樣操作就行. (這里每個對象都有一個mark屬性, true為活動對象)

標記完了, 那就剩下壓縮操作了. 如何進行呢?

  1. 遍歷堆, 將所有活動對象挪到左邊. 但是, 后面有對象引用了前邊的對象, 你就找不到新的指針了, 因為那塊地址很可能已經被覆蓋了.

  2. ....

最后想了想, 還是得老老實實地三步走:

  1. 遍歷堆, 將所有對象通過計算得到新的地址並保存

  2. 遍歷堆, 將所有子對象的地址更新為新的地址, 同時更新根集合中的指針.

  3. 遍歷堆, 將對象集體遷移. 指針的問題都解決了, 可以將對象搬到新家了.

步驟一: 計算所有對象的新地址

// HEAP_START 是堆的開始位置, HEAP_END 是堆得結束位置
obj = HEAP_START
newAddr = HEAP_START
// 遍歷所有活動對象
while(obj < HEAP_END){
    // 非活動對象, 跳過
    if(obj.mark != true){
        obj += obj.size;
        continue;
    }
    // 記錄新的地址
    obj.newAddr = newAddr
    newAddr += obj.size
    // 繼續遍歷
    obj += obj.size
}

 

這遍完后, 所有活動對象都保存了自己的新地址, 然后就可以將所有指針的地址進行更新了.

步驟二: 更新所有指針

// 更新根集合中的指針
for(obj in roots){
    obj = obj.newAddr
}
/*
更新所有活動對象的指針
當然, 這里也可以修改為遍歷所有活動對象, 並將指針進行更新. 但是會出現各種重復處理、指針覆蓋等問題, 就直接遍歷堆了. 
*/
obj = HEAP_START
while(obj < HEAP_END){
    if(obj.mark != true){
        obj += obj.size;
        continue;
    }
    // 更新子對象
    for(child in children){
        child = child.newAddr
    }
    obj += obj.size
}

 


至此, 所有指針都已經更新完畢, 但是, 對象還沒有移動. 只剩下最后一步了, 將對象按照步驟一的規律, 向左排排坐就好啦.

步驟三: 遷移對象

obj = newAddr = HEAP_START
while(obj < HEAP_END){
    if(obj.mark != true) {
        obj += obj.size;
        continue;
     }
    // 將obj的數據復制到newAddr處
    copyData(newAddr, obj, obj.size);
    // 清空數據, 為下一次GC做准本
    newAddr.mark = false;
    newAddr.newAddr = null;
    // 遍歷下一個對象
    obj += obj.size
    newAddr += obj.size
}

 

至此, 實現基本完成. 創建對象分配內存的操作與復制算法一樣. 這個算法簡直是融合了標記清除復制算法的優點, 解決了他們的問題, 不光堆的使用效率變高了, 而且也沒有內存碎片的問題了. 但是, 就是, 只不過要對堆進行三次遍歷而已. 不過沒關系啦, 畢竟有失才有得嘛. 不過是時間換空間了.

而這, 也是標記壓縮算法最大的問題了, 執行時間太久了, 標記清除對堆進行一次遍歷, 而標記壓縮要進行三次. 三倍的時間. 可想而知.

不過也有偉人說了, 算法沒有好不好, 只有是否適合. 這幾種可達性的算法各有優劣吧.

標記壓縮的衍生

Two-Finger算法

將堆的遍歷次數減少到兩次.

如上圖所示, 在第一次遍歷的時候, 指針1從前向后尋找空閑地址, 指針2從后向前尋找活動對象, 找到后在原地址中記錄新地址, 並將對象進行復制.

第二次遍歷就可以將所有對象中的指針進行更新了.

你也發現了, 這個算法如果不想發生內存碎片化, 那就只能令每個對象的空間都是相同的. 而事實上也確實是這樣. 強行規定每個對象都占用相同大小的空間, 我不知道這算法有什么應用場景. (原諒我的無知)

其他

還有一些其他的表格算法lmmixGC算法等, 因為這兩個我看的似懂非懂, 就不細說了.


標記壓縮算法差不多就這么些. 告辭~~~


免責聲明!

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



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