« 上一篇下一篇 »

部分回收概述

一个基本的事实是,对象通常“英年早逝”。们发现,通常80%-98%的新分配对象在几百万条指令之内,或者在再分配了另外的几兆字节之前就消亡了。也就是说,对象通常在垃圾回收过程启动之前就已经变得不可达了。因此,频繁地对新对象进行垃圾具有相当高的性价比。

然而,经历了一次回收的对象很可能在多次回收之后依然存在。在迄今为止描述的垃圾回收器中,同一个成熟对象会在各轮垃圾回收中被发现是可达的。如果使用拷贝回收器,这些对象会在各轮垃圾回收中被一次次地拷贝。世代回收在包含最年轻对象的堆区域中的回收工作最为频繁,所以它通常可以用相对较少的工作量回收大量的垃圾。另一方面,列车算法没有在年轻对象上花费太多的时间,但是它能够有效限制因垃圾回收而造成的程序停顿时间。因此,将这两个策略合并的好方法是对年轻对象使用世代回收,而一旦一个对象变得相当成熟,则将它“提升”到一个由列车算法管理的独立堆区中。

我们把将在一轮部分回收中被回收的对象集合称为目标(target)集,而将其他对象称为稳定 (stable)集。在理想状态下,一个部分回收器应该回收目标集中所有无法从根集到达的对象。然 而,这么做需要跟踪所有的对象,而这正是我们首先要试图避免的事情。实际上,部分回收器只

是保守地回收那些无法从根集和稳定集到达的对象。因为稳定集中的一些对象自身也是不可达的,我们可能会把目标集中一些实际上不存在从根集开始的路径的对象当成可达对象。

我们可以修改描述那样的垃圾回收器,改变“根集”的定义,使之以部分回收的方式工作。现在根集指的不仅是存放在寄存器、栈和全局变量中的对象,它还包括所有指向目标集对象的稳定集中的对象。从一个目标对象指向其他目标对象的引用按照以前的方法进行跟踪,以找到所有的可达对象。我们可以忽略所有指向稳定对象的指针,因为在本轮部分回收中这些对象被认为是可达的。

为了找出那些引用了目标对象的稳定对象,我们可以采用和增量垃圾回收所用技术类似的方法。在增量回收中,我们需要在跟踪过程中记录所有对从已扫描对象到未被访问对象的引用的写运算。在这里,我们需要记录下增变者的整个运行过程中对从稳定对象到目标对象的引用的写运算。只要增变者将一个指向某个目标对象的引用保存到稳定对象中时,我们要么记录下这个引用,要么记录下写人的位置。我们把保存了从稳定对象到目标对象的引用的对象集合称为被记忆集合(remembered set)。我们可以只记录下包含了被写人对象所在的卡片或页,以压缩被记忆集合的表示。

部分垃圾回收器通常被实现为拷贝垃圾回收器。通过使用链表来跟踪可达对象,也可以实现成为非拷贝回收器。下篇文章描述的“世代”方案是一个关于如何将拷贝和部分回收相结合的例子。

« 上一篇下一篇 »