TFdream / jvm-learning

深入拆解Java虚拟机 学习笔记

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

为什么G1 GC从JDK 9之后成为默认的垃圾回收器?

TFdream opened this issue · comments

【转载】腾讯云 - 为什么G1 GC从JDK 9之后成为默认的垃圾回收器?:https://cloud.tencent.com/developer/article/1429131

从JDK 9开始G1替代并行垃圾回收器成为JVM中默认的垃圾回收器(具体可见JEP提案248),并且官方将CMS标记为丢弃(具体可见JEP提案291)。G1能够脱颖而出,成为最大的赢家,其最主要的原因就是在过去几年间,众多使用者使用G1之后发现G1的性能表现的非常优秀。

目前JVM提供的正式垃圾回收器有串行、并行、CMS和G1。这四种垃圾回收器都是基于分代内存管理。分代管理就是把内存划分成不同的区域进行管理,其**来源是:有些对象存活的时间短,有些对象存活的时间长,把存活时间短的对象放在一个区域管理,把存活时间长的对象放在另一个区域管理,那么可以为两个不同的区域选择不同的算法,加快垃圾回收的效率。我们假定内存被划分成2个代,新生代和老年代。把容易死亡的对象放在新生代,新生代通常采用复制算法回收,把预期存活时间较长的对象放在老年代,老年代通常采用标记清除算法

虽然串行、并行、CMS和G1都是采用了分代的内存管理,垃圾回收时采用的算法也都是复制算法或者标记清除算法。但是每一种垃圾回收器的实现并不相同。其主要的区别可以总结为:

  • 内存管理方式不同;
  • 回收算法实现不同;

内存管理

在并行、串行以及CMS中针对堆空间的管理方式都是连续的。如下图所示:
image

连续的内存将导致垃圾回收时收集时间过长,停顿时间不可控。在某些场景中因垃圾回收导致应用程序暂停超过数分钟、数秒等屡见不鲜。

所以G1将堆拆成一系列的分区(Heap Region),这样在一个时间段内,大部分的垃圾回收操作就只是针对一部分分区执行,而不是整个堆或整个(老年)代,从而满足在指定的停顿时间内完成垃圾回收的动作。G1内存分区如下图所示:

image

在G1新生代就是一系列的内存分区,这意味着不用再要求新生代是一个连续的内存块。类似地,老生代也是由一系列的分区组成。在JVM运行时,从内存管理角度不需要预先设置分区是老生代分区还是新生代分区,而是在内存分配时决定,当新生代需要空间则分区被加入到新生代中,当老生代需要内存空间则分区被加入到老生代中。事实上,G1通常的运行状态是:映射G1分区的虚拟内存随着时间的推移在不同的代之间切换。例如一个G1分区最初被指定为新生代,经过一次新生代的回收之后,整个新生代分区都被划入到待使用的分区中,那它就可以作为新生代分区使用,也可以作为老生代分区使用。很可能在完成一个新生代回收之后,一个新生代的分区在未来的某个时刻被用于老生代分区。同样地,在一个老生代分区完成回收之后,它就成为待使用分区,在未来某个时候作为一个新生代分区来使用。

G1新生代的回收方式是并行回收,采用复制算法。与其他JVM垃圾回收器一样,一旦发生一次新生代回收,整个新生代都会被回收。这也就是我们常说的新生代回收(Young GC,简称为YGC)。

但是G1和其他垃圾回收器不同的地方在于:

  • 一、G1会根据预测时间动态的改变新生代的大小(G1中预测时间是根据运行垃圾回收的历史数据通过数学建模预测得到,所用的数学模型是衰减平均);
  • 二、G1老生代的垃圾回收方式与其他JVM垃圾回收器对老生代处理有着极大的不同。G1老生代的回收不会为了释放老生代的空间对整个老生代做回收。相反,在任意时刻只有一部分老生代分区会被回收,并且,这部分老生代分区将在下一次增量回收时与所有的新生代分区一起被回收。这就是我们所说的混合回收(Mixed GC),在选择老生代分区的时候,优先考虑垃圾多的分区。

回收算法

1、串行垃圾回收器

使用单线程进行垃圾回收,在回收的时候应用程序需要暂停执行。新生代通常采用复制算法,老生代通常采用标记压缩算法。串行回收典型的垃圾回收活动图如下所示:

image

2、并行回收回收器

使用多线程进行垃圾回收,在回收的时候应用程序需要暂停,新生代通常采用复制算法,老生代通常采用标记压缩算法。垃圾回收活动图如下:

image

3、CMS

整个回收期间划分成多个阶段:初始标记、并发标记、重新标记、并发清除等阶段。在初始标记和重新标记阶段需要暂停应用程序,在并发标记和并发清除期间可以和应用程序并发运行。这个算法通常适用于老生代,新生代可以采用并行复制回收,也可以采用串行复制算法。垃圾回收活动图如下:
image

同样在老生代回收时,因为是并发执行,当在内存分配时发现如果内存不足需要进行Full GC,也需要STW对整个内存进行串行回收。

G1

G1新生代的收集方式是并行收集,采用复制算法。与其他JVM垃圾回收器一样,一旦发生一次新生代回收,整个新生代都会被回收。这也就是我们常说的新生代回收(Young GC,简称为YGC)。但是G1和其他垃圾回收器不同的地方在于:一、G1会根据预测时间动态的改变新生代的大小;二、G1老生代的垃圾回收方式与其他JVM垃圾回收器对老生代处理有着极大的不同。G1老生代的收集不会为了释放老生代的空间对整个老生代做回收。相反,在任意时刻只有一部分老生代分区会被回收,并且,这部分老生代分区将在下一次增量回收时与所有的新生代分区一起被收集。这就是我们所说的混合回收(Mixed GC),在选择老生代分区的时候,优先考虑垃圾多的分区。

老生代分区的选择就涉及到G1的并发标记算法,这个过程称为“并发标记阶段”,并发标记是是指并发标记线程和应用程序线程同时运行,它有4个典型的子阶段:初始标记子阶段、并发标记子阶段、再标记子阶段和清理子阶段。