Java中垃圾收集器都有哪些?又是如何配合进行垃圾回收的?它们的使用场景是什么? — JVM系列(九)

前言

垃圾回收算法,我们已经了解过。

那么有哪些垃圾收集器?它们的使用场景是什么?

它们又是如何运用垃圾回收算法来进行垃圾回收的呢?

一、Serial收集器

新生代的收集器。采取的复制算法。

单线程的收集器,它只会使用一个CPU或一条收集线程去完成垃圾收集工作。

在它进行垃圾收集时,必须暂停其他所有的用户线程,直到它收集结束。

HotSpot虚拟机为消除或者减少工作线程因内存回收而导致停顿的努力一直在进行着。

Serial收集器是虚拟机运行在Client模式下的默认新生代收集器。也是比较好的选择。

二、ParNew收集器

ParNew收集器其实就是Serial收集器的多线程版本。也就是虚拟机会使用多线程来进行垃圾回收,但仍然是停止所有的用户线程。

它是许多运行在Server模式下的虚拟机中首选的新生代收集器。

可以与CMS老年代的垃圾收集器配合使用。

ParNew收集器在单CPU的环境中绝对不会有比Serial收集器更好的效果。当然随着使用的CPU的数量的增加,它对于GC时系统资源的有效使用还是很有好处的。

默认开启的线程数与CPU数量相同。可以使用参数来指定垃圾收集的线程数目。

三、Parallel Scavenge—吞吐量优先

新生代的收集器,使用复制算法。并行的线程收集器。

关注点与其他收集器不同,其他收集器的关注点是尽可能的缩短垃圾收集时用户线程的停顿时间。

而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。

所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值。

吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),例如每100分钟的时间里,需要1分钟的时间做垃圾收集,那么吞吐量就是99%。

停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。

高吞吐量则可以高效地利用CPU时间,尽快完成程序的运算任务。主要适合在后台运算而不需要太多交互的任务。

Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,它是吞吐量优先的收集器。

四、Serial Old收集器

是Serial 收集器的老年代版本,也是一个单线程的收集器。

使用标记—整理算法。

这个收集器的主要意义也是在于给Client模式下的虚拟机使用。

五、Parallel Old收集器

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

在注重吞吐量及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。

六、CMS收集器

以获取最短回收停顿时间为目标的收集器。给用户带来较好的体验。并发收集,低停顿。

使用标记—清除算法实现的。

整个过程分为4个步骤:

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

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

CMS收集器的缺点:

  1. CMS无法处理浮动垃圾:因为并发清除阶段,还会有新的垃圾产生,但是本次无法清除,称为浮动垃圾。
  2. 基于标记—清除算法实现,会有大量空间碎片产生,将会给大对象分配带来很大麻烦,即使老年代有很多剩余空间,但是无法找到足够大的连续空间来分配当前对象。

七、G1收集器

G1是一款面向服务端应用的垃圾收集器,具备以下特点:

  • 在GC收集的时候,用户线程仍然可以继续执行。
  • G1中仍然有分代的概念,但是会采用不同的方式去处理新建对象和已经存活了一段时间的对象。
  • 整体上来看是基于标记—整理算法实现的收集器,从局部(两个Region之间)上来看是基于复制算法实现的。这都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。
  • 可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在收集上的时间不得超过N毫秒

G1将整个Java堆划分为多个大小相等的独立区域Region,虽然还保留新生代与老年代的概念,但新生代和老年代不再是物理隔离的了。它们都是一部分Region(不需要连续)的集合。

G1之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集,它跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。

在G1收集器中,Region之间的对象引用,以及其他收集器中的新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中的每个Region都有一个与之对应的Remembered Set,记录对象被哪些地方引用,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下几个步骤:

  1. 初始标记:Stop The World。
  2. 并发标记:可以与用户进程并发。
  3. 最终标记:Stop The World。
  4. 筛选回收:Stop The World。首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。

总结

一表胜前言:

新生代/老年代 Serial Old—老年代 Parallel Old—老年代 CMS—老年代 G1
Serial—新生代 Y N Y N
ParNew—新生代 Y N Y N
Parallel Seavenge—新生代 Y Y N N
G1 N N N Y

以上表格是垃圾收集器的一个搭配总结,可以搭配使用的是Y,不可以搭配使用的是N。

从表格中可以看到:

  • G1是特殊的垃圾收集器,新生代与老年代都使用。
  • 老年代的收集器Serial Old,除了G1无法搭配,其他的新生代都可以与之配合使用。
  • 老年代的收集器Parallel Old,只能与Parallel Seavenge的新生代收集器配合使用。
  • 老年代收集器CMS既可以与新生代Serial,也可以与ParNew配合使用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章