Java中的垃圾回收

前面我们已经提到,对象间可能会存在跨代引用,因此最直观的做法便是扫描其它分区到伊甸区的所有引用。但不幸的是这么做会做成分代的做法变得毫无意义。JVM对此有它自己的妙招:卡片式标记(card-marking)。基本的做法是,JVM将伊甸区中可能存在老生代引用的对象标记为”脏”对象。关于这点 Nitsan的博客 这里有更进一步的介绍。

标记完成后,所有存活对象会被复制到其中的一个存活区。于是整个伊甸区便可认为是清空了,又可以重新用来分配对象了。这一过程便被称为”标记复制“:存活对象先被标记,随后被复制到存活区中。

存活区(Survivor)

紧挨着伊甸区的是两个存活区,分别是from区和to区。值得一提的是其中的一个存活区始终都是空的。

空的存活区会在下一次新生代GC的时候迎来它的居民。整个新生代中的所有存活对象(包含伊甸区以及那个非空的名为from的存活区)都会被复制到to区中。一旦完成之后,对象便都跑到to区中而from区则被清空了。这时两者的角色便会发生调转。

存活对象会不断地在两个存活区之间来回地复制,直到其中的一些对象被认为是已经成熟,“足够老”了。请记住这点,基于分代假设,已经存活了一段时间的对象,在相当长的一段时间内仍可能继续存活。

这些“年老”的对象会被提升至老年代空间。出现对象提升的时候,这些对象则不会再被复制到另一个存活区,而是直接复制到老年代中,它们会一直待到不再被引用为止。

垃圾回收器会跟踪每个对象历经的回收次数,来判断它们是否已经“足够年老”,可以传播至老年代中。在一轮GC完成之后,每个分区中存活下来的对象的计数便会加一。当一个对象的年龄超过了一个特定的年老阈值之后,它便会被提升到老年代中。