标签:class 基本 迁移 压缩 update 保存 适合 包含 compute
其分为两个阶段标记阶段,和压缩阶段.其中标记阶段和标记清除算法的标记阶段是一样的.
在压缩阶段,由于要移动可达对象,那么就要考虑移动对象时候的顺序问题,一般分为一下三种:
其实质是任意顺序移动,其最适合处理包含固定大小对象的内存区域.
该算法需要遍历堆内存两次,第一次是将堆尾的可达对象移动到堆开始的空闲内存单元(所以在前面说,最适合固定对象长度).第二次遍历则需要修改可达对象的引用,因为一些可达对象已经被移动到别的地址,而原先引用它们的对象还指向之前的地址.在这两次的遍历过程中,首位两个指针分别从对的头尾两个位置向中间移动,直至两个指针相遇.
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)
指针free和scan同时指向起始堆位置,同时scan开始向堆尾移动,目的是要找到被标记的可达对象.
同理,在可达对象D处分配一块空间来保存对象D应该迁移到的位置,由于B对象已经占用了2个内存单元,所以对象E的迁移地址是从位置2开始,也就是当前free指针指向的位置。
指针free,scan继续向前移动。
第一次遍历完后,所有的可达对象都有了对应的迁移地址,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引用对象B,对象B的迁移地址为0,于是collector将根对象对B对象的引用指向它的迁移地址 - 位置0, 现在A对象所处的位置。
同理,对于根对象2,3都执行同样的操作,将它们对其所引用的对象的引用修改为对应的它们所引用的对象的迁移地址。
通过scan指针遍历堆内存,更新所有的可达对象对其引用对象的引用为其引用对象的迁移地址。比如说,对于可达对象B,它引用了对象D,D的迁移地址是2,那么B直接将其对D对象的引用重新指向2这个位置。
第二次遍历结束后的对象之间的引用关系。
第三次遍历则是根据可达对象的迁移地址去移动可达对象,比如说可达对象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)
标签:class 基本 迁移 压缩 update 保存 适合 包含 compute
原文地址:https://www.cnblogs.com/A-FM/p/10908285.html