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 }