HotSpot垃圾收集器(截止到Java 12)

目录

1 Serial / Serial Old收集器

2 ParNew收集器

3 Parallel Scavenge / Parallel Old收集器

4 CMS收集器

5 G1收集器

6 ZGC收集器

7 Epsilon收集器

8 Shenandoah收集器


如果说垃圾收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现(以下垃圾收集器介绍仅限于HotSpot虚拟机


1 Serial / Serial Old收集器

串行收集器是最古老、最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。新生代、老年代使用串行回收;垃圾收集的过程中会“Stop The World”(服务暂停)。

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。


2 ParNew收集器

ParNew收集器其实就是Serial收集器的多线程版本。除了使用多线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、“Stop The World”、对象分配规则、回收策略等都与Serial收集器完全一样。


3 Parallel Scavenge / Parallel Old收集器

Parallel Scavenge收集器是一个新生代收集器,它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。


4 CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。从名字上就可以看出CMS收集器是基于标记-清除算法实现的,收集的范围是老年代。整个过程分为4个步骤:包括:

  • 初始标记(CMS initial mark)
  • 并发标记(CMS concurrent mark)
  • 重新标记(CMS remark)
  • 并发清除(CMS concurrent sweep)

其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Root Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行。

优点:并发收集、低停顿

缺点:产生大量空间碎片、并发阶段会降低吞吐量(可以选择开启碎片整理功能)


5 G1收集器

上面提到的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。

G1收集器的运作过程与CMS较为相似,其过程如下:

  • 初始标记(Initial Marking):仅标记一下GC Roots能直接关联到的对象,且修改TAMS(Next Top at Mark Start),让下一阶段并发运行时,用户程序能在正确可用的Region中创建新对象。需要“Stop The World”,但速度很快。
  • 并发标记(Concurrent Marking):进行GC Root Tracing的过程,上一步产生的集合中标记出存活对象。耗时较长,但应用程序也在运行。并不能保证可以标记出所有的存活对象。
  • 最终标记(Final Marking):为了修正并发标记期间,因用户程序继续运作而导致标记变动的那一部分对象的标记记录。需要“Stop The World”,且停顿时间比初始标记稍长,但远比并发标记短。采用多线程并行执行来提升效率。
  • 筛选回收(Live Data Counting and Evacuation):首先排序各个Region的回收价值和成本,然后根据用户期望的GC停顿时间来制定回收计划。最后按计划回收一些价值高的Region中的垃圾对象。从局部上来看,回收时采用复制算法,从一个或多个Region中复制存活对象到堆上的另一个空的Region,并且在此过程中压缩和释放内存。可以并发进行,降低停顿时间,并增加吞吐量。

不同于CMS使用的标记-清除算法,G1从整体来看是基于标记-整理算法实现的收集器,从局部上来看是基于“复制”算法实现的。将一个region中的存活对象复制到另一个region中,这种不会像CMS那样回收完因为有很多内存碎片还需要整理一次,G1采用复制算法回收几乎不会有太多内存碎片。

G1相对于CMS另外一个更大的优势,就是可以设置可预测的停顿模型,能够使开发者明确指定在长度为M毫秒的时间片段内,消耗在垃圾收集器上的时间不能超过N毫秒。

需要说明的一点是,从Java 9开始,默认的垃圾收集器已经改为了G1,代替了之前的Parallel Scavenge(新生代) + Parallel  Old(老年代)的组合。


6 ZGC收集器

ZGC收集器是从Java 11开始支持的实验性垃圾收集器,估计等到正式商用还要有个几年的时间。其目标如下:

  • GC暂停时间不应超过10毫秒
  • 处理堆的大小从相对较小(几百兆字节)到非常大(多太字节)不等
  • 与使用G1相比,应用程序吞吐量减少不超过15%
  • 为未来的GC功能和优化利用彩色指针和负载障碍奠定基础
  • 最初支持的平台:Linux / x64

7 Epsilon收集器

Java 11还加入了一个比较特殊的垃圾收集器——Epsilon,该垃圾收集器被称为“no-op”收集器,将处理内存分配而不实施任何实际的内存回收机制。 也就是说,这是一款不做垃圾回收的垃圾回收器。这个垃圾回收器看起来并没什么用,主要可以用来进行性能测试、内存压力测试等,Epsilon GC可以作为度量其他垃圾回收器性能的对照组。大神Martijn说,Epsilon GC至少能够帮助理解GC的接口,有助于成就一个更加模块化的JVM。


8 Shenandoah收集器

Java 12中新加入的垃圾收集器,Shenandoah最初的目标是把GC停顿时间降到10毫秒以下,并且对内存的支持扩展到TB级别。为了降低停顿时间,回收器需要使用更多的线程来并行处理回收任务。而要在降低停顿时间的同时能够支持更大的堆空间,回收器对CPU的多核处理能力提出了更高的要求。相比于CMS和G1,Shenandoah不仅进行并行的垃圾标记,在压缩堆空间时也是并行进行的。

值得一提的是,作为首个由非Oracle开发,由RedHat领导开发的垃圾收集器,其目标又与Oracle在Java 11中发布的ZGC几乎完全一致,两者天生就存在竞争。于是Oracle马上用实际行动抵制了这个新收集器,在OracleJDK 12里把Shenandoah的代码通过条件编译强行剔除掉(OpenJDK 12中仍然可用)。

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