7.6.5 拷贝回收器

拷贝回收器(Copying Garbage Collector)是一种常见的垃圾回收算法,通常用于实现新生代的垃圾回收。与标记-清扫式或标记并压缩算法不同,拷贝回收器将堆内存分为两个区域:From区和To区。在每次垃圾回收时,它将存活的对象从From区拷贝到To区,并且清空From区,以便下一次垃圾回收时重复利用。以下是拷贝回收器的基本工作流程:

  1. 初始分配: 堆内存被分为两个相等大小的区域:From区和To区。一开始,From区是用来存放对象的,而To区是空的。

  2. 对象分配: 当程序需要分配新的对象时,会将对象分配到From区。From区用于存放新创建的对象。

  3. 垃圾回收: 当From区的空间不足时,触发一次垃圾回收。垃圾回收器会遍历From区中的所有对象,并且标记所有可达的对象。然后,它将所有可达的对象复制到To区,并且清空From区。

  4. 对象更新: 在拷贝过程中,所有被拷贝的对象的引用也需要更新。如果被拷贝的对象引用了其他对象,那么引用将会指向To区中的对象。

  5. 区域交换: 当拷贝完成后,From区和To区的角色互换。即,To区成为新的From区,From区成为新的To区。这样,下一次垃圾回收时,就会使用To区作为新的From区。

通过拷贝回收器,可以解决新生代中的内存碎片化问题。因为每次垃圾回收后,所有存活的对象都会被紧凑地复制到To区,而From区则被完全清空,因此不会出现内存碎片。拷贝回收器也相对简单高效,因为它只需要在From区进行垃圾回收,并且拷贝存活对象到To区,而不需要进行标记、清扫或者压缩等复杂的操作。

--------------------------------------------------------------------

摘自编译原理文章片段:

现在我们可以考虑这个算法本身了。第(2)行确保From 空间中的所有对象都还没有新地址。在第(3)行中,我们初始化两个指针 unscanned 和fee,使它们都指向 To 半空间的开始位置。指针fee 将总是指向 7o 半空间中空闲空间的起始位置。当我们往 " 空间加人对象时,那些地址低于unscanned的对象将处于已扫描状态,而那些位于unscanned 和fee 之间的对象则处于待扫描状态。因此,fee 总是在 uscunmed的前面。当后者追上前者时就表示不存在更多的待扫描对象了我们就完成了垃圾回收工作。请注意,我们是在T 空间中完成垃圾回收工作的,尽管在第(8)行中检查的对象中的所有引用都是指向 From 空间的。

这段描述的是拷贝回收器的工作原理,具体来说,是在描述如何在To区域中完成垃圾回收工作,尽管待处理对象的引用都是指向From区域的情况。

让我们逐句解释:

  1. 第(2)行确保From空间中的所有对象都还没有新地址。

    • 这句话指的是在垃圾回收开始之前,From空间中的所有对象都应该还没有被拷贝到To空间中,它们的引用仍然指向From空间。

  2. 在第(3)行中,我们初始化两个指针unscanned和fee,使它们都指向To半空间的开始位置。指针fee将总是指向To半空间中空闲空间的起始位置。

    • 这段描述说明了在初始化拷贝回收器时,两个指针unscanned和fee被初始化为指向To空间的起始位置,其中unscanned指向未扫描的位置,fee指向未使用的空间的起始位置。

  3. 当我们往"空间加人对象时,那些地址低于unscanned的对象将处于已扫描状态,而那些位于unscanned和fee之间的对象则处于待扫描状态。因此,fee总是在unscanned的前面。

    • 这句话解释了在拷贝回收器工作期间,随着对象被拷贝到To空间,unscanned指针会向后移动,将拷贝过的对象标记为已扫描,而fee指针则始终指向未使用的空间的起始位置,确保待处理对象位于unscanned和fee之间。

  4. 当后者追上前者时就表示不存在更多的待扫描对象了,我们就完成了垃圾回收工作。

    • 当unscanned指针追上fee指针时,表示To空间中已经没有更多待扫描的对象了,即所有存活对象都已经被拷贝到To空间,并且所有引用也已经更新为指向To空间的地址,此时垃圾回收工作完成。

总体来说,这段描述了拷贝回收器如何在To空间中完成垃圾回收工作,并且解释了unscanned和fee两个指针在这个过程中的作用。尽管对象的引用指向From空间,但是拷贝回收器会将对象拷贝到To空间,并且更新所有引用为指向To空间的地址。

 

---------------------------------------------------------------------------

摘自编译原理文章片段:

第(4)行和第(5)行处理可以从根集访问到的对象。请注意,因为函数副作用,在第(5)行中对 LookupNeuwLocation 的某些调用会在 "中为这些对象分配存储块,同时增加,fee 指针的值。因此,除非没有被根集引用的对象(在这种情况下,整个堆区都是垃圾),当程序第一次运行到这里时将进入第(6)行到第(10)行的循环。然后,这个循环扫描所有已经被加入到 To 空间中并处于待扫描状态的对象。第(7)行处理下一个待扫描的对象。。在第(8)、(9)行,对于o中的每个引用,从它在From 半空间中的原值被翻译为在 To半空间中的值。

这段描述说明了在拷贝回收器的工作过程中,当处理根集引用到的对象时,可能会触发新对象的分配,进而增加fee指针的值。我将逐句解释:

  1. 第(4)行和第(5)行处理可以从根集访问到的对象。

    • 这句话指的是拷贝回收器在处理根集引用到的对象时,执行的操作。根集是程序中的一组初始引用,通常包括全局变量、栈中的引用等。

  2. 因为函数副作用,在第(5)行中对LookupNeuwLocation的某些调用会在中为这些对象分配存储块,同时增加fee指针的值。

    • 这句话说明了在执行第(5)行中的某些操作时,可能会触发函数LookupNeuwLocation的调用,这个函数会为新对象分配存储块,并且增加fee指针的值。函数的副作用指的是除了返回值外,还会对其他变量或状态产生影响。

  3. 除非没有被根集引用的对象(在这种情况下,整个堆区都是垃圾),当程序第一次运行到这里时将进入第(6)行到第(10)行的循环。

    • 这句话指的是在程序第一次运行到处理根集引用的代码段时,如果没有根集引用的对象,那么整个堆区都被认为是垃圾,垃圾回收工作就可以直接跳过。否则,程序将进入第(6)行到第(10)行的循环。

  4. 然后,这个循环扫描所有已经被加入到To空间中并处于待扫描状态的对象。

    • 这句话指的是循环会扫描所有已经被加入到To空间中并处于待扫描状态的对象,即unscanned和fee指针之间的对象。

  5. 第(7)行处理下一个待扫描的对象。

    • 这句话说明了循环中的第(7)行代码的作用,即处理下一个待扫描的对象。在这个过程中,unscanned指针会向后移动。

  6. 在第(8)、(9)行,对于o中的每个引用,从它在From半空间中的原值被翻译为在To半空间中的值。

    • 这句话解释了第(8)和(9)行代码的作用,即更新对象中的引用,使其指向To半空间中对应的对象。由于对象已经被拷贝到To空间,所以需要更新引用。

  7. 请注意,因为函数副作用,如果解释下这句话。

    • 这句话强调了在处理引用更新时可能会发生的函数副作用,例如函数的调用可能会影响其他变量或状态。在这个上下文中,函数的副作用可能会导致新对象的分配,进而增加fee指针的值。

7.6.6 四种垃圾收集器开销的比较


Cheney 算法的优势在于它不会涉及任何不可达对象。另一方面,拷贝垃圾回收器必须移动所有可达对象的内容。对于大型对象,或者那些经历了多轮垃圾收集过程的生命周期长的对象而言,这个过程的开销特别高。我们对本节给出的四种算法的运行时间进行总结。下面的每个估算都忽略了处理根集的开销。
。基本的标记-清扫式算法(算法7.12):与堆区中存储块的数目成正比。
Baker 的标记-清扫式算法(算法7.14):与可达对象的数目成正比。#
基本的标记并压缩算法(算法7.15):与堆区中存储块的数目和可达对象的总大小成正比。
。Cheney 的拷贝回收器(算法7.16):与可达对象的总大小成正比。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章