copying() { $free = $to_start for (r : $root) *r = copy(r) // 注意,這裏實際是一個update,將對象替換 swap($from_start, $to_start) }
// 這裏,傳給copy的對象,必定是舊堆的 copy(obj) { if obj.tag != COPIED // 重要,在老堆的對象中,標誌這對象已經拷走。 copy_data($free, obj, obj.size) // 拷貝到free obj.tag = COPIED obj.forwarding = $free // 重要!obj是老堆中對象,forwarding字段指向了新堆中的同一對象!! $free += obj.size for (child : children(obj.forwarding) child = copy(child) //重要,又是一個update操做,不單單是遞歸處理,也是遞歸賦值。將老的 return obj.forwarding // 最終返回 }
與此相對,分配變得更簡單:算法
new_obj(size) { if ($free + size > $from_start + HEAD_SIZE/2) copying() // 超過一半觸發 if ($free + size > $from_start + HEAP_SIZE/2) fail() obj = $free // 分配直接從指針處取內存! obj.size = size $free += size return obj }
以上即是複製算法,可見不復雜。緩存
copying() { scan = $free = $to_start // scan是一個邏輯隊列的頭,free則是這個邏輯隊列的尾 for (r : $root) r = copy(r) // 從root出發,先走一層。拉開scan與free的差距,理解成邏輯上將root的第一層子節點入隊列。具體要看後面copy實現 while (scan != free) for (child : children(scan)) child = copy(child) scan += scan.size swap($from_start, $to_start) } copy(obj) { if !(obj.forwarding belong $to_start) // 此處能夠直接拿forwarding字段來判斷是否已經拷貝到新堆空間 copy_data($free, obj, obj.size) obj.forwarding = $free // 老對象指向新對象 $free += obj.size return obj.forwarding }