JVM G1源码分析和调优
上QQ阅读APP看书,第一时间看更新

1.4.4 垃圾优先回收

垃圾优先回收器(Garbage-First,也称为G1)从JDK7 Update 4开始正式提供。G1致力于在多CPU和大内存服务器上对垃圾回收提供软实时目标和高吞吐量。G1垃圾回收器的设计和前面提到的3种回收器都不一样,它在并行、串行以及CMS GC针对堆空间的管理方式上都是连续的,如图1-7所示。

图1-6 并发标记回收

图1-7 连续空间管理

连续的内存将导致垃圾回收时收集时间过长,停顿时间不可控。因此G1将堆拆成一系列的分区(Heap Region),这样在一个时间段内,大部分的垃圾收集操作只针对一部分分区,而不是整个堆或整个(老生)代,如图1-8所示。

图1-8 分区空间管理

在G1里,新生代就是一系列的内存分区,这意味着不用再要求新生代是一个连续的内存块。类似地,老生代也是由一系列的分区组成。这样也就不需要在JVM运行时考虑哪些分区是老生代,哪些是新生代。事实上,G1通常的运行状态是:映射G1分区的虚拟内存随着时间的推移在不同的代之间切换。例如一个G1分区最初被指定为新生代,经过一次新生代的回收之后,会将整个新生代分区都划入未使用的分区中,那它可以作为新生代分区使用,也可以作为老生代分区使用。很可能在完成一个新生代收集之后,一个新生代的分区在未来的某个时刻可用于老生代分区。同样,在一个老生代分区完成收集之后,它就成为了可用分区,在未来某个时候可作为一个新生代分区来使用。

G1新生代的收集方式是并行收集,采用复制算法。与其他JVM垃圾回收器一样,一旦发生一次新生代回收,整个新生代都会被回收,这也就是我们常说的新生代回收(Young GC)。但是G1和其他垃圾回收器不同的地方在于:

·G1会根据预测时间动态改变新生代的大小。

其他垃圾回收新生代的大小也可以动态变化,但这个变化主要是根据内存的使用情况进行的。G1中则是以预测时间为导向,根据内存的使用情况调整新生代分区的数目。

·G1老生代的垃圾回收方式与其他JVM垃圾回收器对老生代处理有着极大的不同。G1老生代的收集不会为了释放老生代的空间对整个老生代做回收。相反,在任意时刻只有一部分老生代分区会被回收,并且,这部分老生代分区将在下一次增量回收时与所有的新生代分区一起被收集。这就是我们所说的混合回收(Mixed GC)。在选择老生代分区的时候,优先考虑垃圾多的分区,这也正是垃圾优先这个名字的由来。后续我们将逐一介绍这些内容。

在G1中还有一个概念就是大对象,指的是待分配的对象大小超过一定的阈值之后,为了减少这种对象在垃圾回收过程的复制时间,直接把对象分配到老生代分区中而不是新生代分区中。

从实现角度来看,G1算法是复合算法,吸收了以下算法的优势:

·列车算法,对内存进行分区,参见图1-8。

·CMS,对分区进行并发标记。

·最老优先,最老的数据(通常也是垃圾)优先收集。

关于列车算法、CMS和最老优先可以参考其他的书籍,这里不再赘述。