java七大垃圾收集器区别汇总

七大垃圾收集器分别是Serial、ParNew、Parallel Scavenge、CMS、Serial Old、Parallel Old、G1,这些收集器都已经开始商用了。

它们之间的关系如下图:

图片来源于:《深入理解Java虚拟机 第3版》

上图中的JDK9表示从JDK9开始,CMS和Serial、ParNew和Serial Old的组合官方不再支持。

这七个垃圾收集器分别作用于不同的分代,如果两个收集器之间存在连线,就说明它们可以搭配使用。唯独G1收集器是可以作用于新生代和老年代。

垃圾收集器发展到今天为止,STW(Stop The World)始终未能彻底消除,即使7个里面最先进的G1,也要有STW。

下表是收集器之间的不同点:

收集器名字是否并行收集作用分代是否STW收集算法优点缺点
Serial单线程串行新生代复制

简单,高效,消耗额外内存最小

无法利用多线程优势,JVM内存不能太大
ParNew多线程并行新生代复制可以并行收集在核心数少的场景下,无法发挥优势
Serial Old单线程串行老年代标记-整理作为CMS收集器失败后的预备方案无法利用多线程优势,收集过程需要STW
Parallel Scavenge多线程并行新生代复制以吞吐量为优化目标,可以自动调节JVM内存各分代大小--
Parallel Old多线程并行老年代标记-整理以吞吐量为优化目标,可以自动调节JVM内存各分代大小--
CMS多线程并发老年代部分阶段标记-清除
以获取最短回收停顿时间为目标,应用范围广
产生内存碎片,有可能出现回收失败
G1多线程并发新生代和老年代部分阶段

新生代:复制

Mixed GC:标记-整理

大内存收集优势明显,不会产生内存碎片,可以指定期望的停顿时间占用额外内存和系统负载均高于CMS,小内存收集CMS优于G1

吞吐量=运行用户代码的时间/(运行用户代码的时间+运行垃圾收集的时间)

前面的收集器原理相对比较简单,下面详细展开介绍CMS和G1。

CMS

CMS全称是 Concurrent Mark Sweep,该收集器是一种以获取最短回收停顿时间为目标的收集器,因为停顿时间短,因此在交互式系统中应用比较广,它的收集过程分为以下四步:
  1. 初始标记(CMS initial mark
  2. 并发标记(CMS concurrent mark
  3. 重新标记(CMS remark
  4. 并发清除(CMS concurrent sweep
1、初始标记
该阶段需要STW,初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。
 
2、并发标记
并发标记阶段就是从 GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
 
3、重新标记
该阶段需要STW,用于修正第二阶段产生变动的那一部分对象的引用关系,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。
 
4、并发清除
清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发。
 
在整个垃圾收集过程中 ,耗时最长是并发标记和并发清除,但是它们可以与用户线程并发执行,因此总体上看CMS的整个收集过程可以认为是与用户线程并发执行的。
 
CMS的缺点
  1. 并发阶段会占用了一部分系统资源,当系统资源整体紧张时,这将造成用户程序变慢,降低处理速度,而且相比较于STW的处理方式,并发阶段的处理时间会拉长,这会造成用户程序长时间响应速度慢。
  2. CMS采用的是标记-清除算法,经过长时间的运行后,内存可能出现大量碎片,这将导致明明内存很充足,但是找不到连续的空间分配给大对象,最后CMS只能触发Full GC,并在Full GC时,合并整理内存碎片,这个整理过程需要移动对象,因此需要STW。java还提供另一个参数-XX:CMSFullGCsBefore-Compaction(此参数从JDK 9开始废弃),这个参数的作用是要求CMS收集器在执行过若干次(数量由参数值决定)不整理空间的Full GC之后,下一次进入Full GC前会先进行碎片整理(默认值为0,表示每次进入Full GC时都进行碎片整理)。
  3. 浮动垃圾无法及时清理,因为清理过程是并发进行的,在标记过程结束以后,可能会有新的垃圾产生,这部分垃圾就是浮动垃圾,CMS无法在当次清理过程处理掉这部分垃圾,只能等到下次清理。
  4. CMS在老年代使用了92%(JDK6的默认值,比例可以通过参数-XX:CMSInitiatingOccupancyFraction调整)的内存后就会触发垃圾收集,这是因为CMS是并发执行,在收集的过程中,CMS也要使用内存。这个比例需要根据生产环境做出合理调整,既不能太高,也不能太低,如果太低,会频繁引发垃圾收集,如果太高,CMS收集期间预留的内存无法满足程序分配新对象的需要,就会出现一次“并发失败”(Concurrent Mode Failure),这时候虚拟机将不得不启动后备预案:冻结用户线程的执行,临时启用Serial Old收集器来重新进行老年代的垃圾收集,但这样停顿时间就很长了。

G1

G1的全称是吞吐量=运行用户代码的时间/(运行用户代码的时间+运行垃圾收集的时间)Garbage First。G1是一个里程碑式的收集器,在JDK9以上,它已经成为默认的收集器。

G1垃圾的收集方式与之前的收集器有很大的不同。G1弱化了分代的概念,它将堆内存划分成了多个大小相等的Region,每个Region根据需要可以作为新生代或老年代的空间,收集器根据Region的角色不同采用不同的收集方法。每个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为1MB~32MB,且应为2的N次幂。内存划分为Region后,新生代和老年代分别有多个Region组成,每个分代里面Region与Region之间不要求物理上连续,分代里面Region的个数G1也可以根据需要动态调整,这样年轻代和老年代的大小便可以动态变化,因此建议不要设置年轻代大小(-Xmn设置年轻代大小),由G1自己调整,网上有介绍说如果设置了-Xmn,将导致设定的收集停顿时间无效。

G1里面除了新生代和老年代之外,还有一个特殊的区域:Humongous区域。该区域是专门存储大对象的,G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象,对于大小超过了单个Region的大对象,G1将选择N个连续的Humongous Region来存放。G1的大多数行为都把Humongous Region作为老年代的一部分来进行看待。

G1相比于之前的垃圾收集器,还有一个特点是用户可以指定每次收集的停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认200毫秒)。G1在收集时,会尽力达到这个时间。那么它是如何做到的?G1将Region作为单次回收的最小单元,这意味着每次回收要么是整个Region全部回收,要么是不回收。G1会跟踪每个Region里面的垃圾堆积的“价值”大小,价值即回收所获得的空间大小以及回收所需时间的经验值,然后在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间,优先处理回收价值收益最大的那些Region。这种使用Region划分内存空间,以及具有优先级的区域回收方式,保证了G1收集器在有限的时间内获取尽可能高的收集效率。

当堆内存的使用量达到一个阈值(通过参数-XX:InitiatingHeapOccupancyPercent指定,默认是45% )后,要触发G1的收集过程。G1收集的过程可划分为以下四个步骤:
  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 筛选回收

G1的收集步骤与CMS类似,下面详细展开介绍每个过程:

初始标记

该阶段仅仅只是标记一下 GC Roots 能直接关联到的对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。该阶段总是伴随一次Minor GC发生。G1的Minor GC与其他收集器的Minor GC没有太大的区别,也是先标记再复制,整个过程需要STW。
 

并发标记

从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。该阶段会被Minor GC打断。
 
最终标记
该阶段用来标记并发标记阶段产生的新垃圾(并发阶段和应用程序一同运行),这一阶段需要 对用户线程做一个短暂的暂停,G1在这一阶段采用SATB算法。SATB叫做并发快照,它是一个用来解决并发过程中由于用户修改引用关系而导致对象可能被误标的方案。
 
筛选回收
负责更新 Region 的统计数据,对各个 Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分 Region 的存活对象复制到空的 Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。
 
从上述四个步骤来看,只有并发标记是与用户线程并发执行,其他的步骤都需要STW。
 
在第四个步骤筛选回收,G1根据设置的停顿时间计算回收哪些 Region,这引来了一个问题,如果停顿时间设置的很小,那么每次回收的Region个数也很少,当分配新对象的速度超过回收速度时,将导致垃圾慢慢堆积,最终导致内存不够使用,引发Full GC,使得性能下降。因此设置停顿时间不能追求越低越好,要设置的合理。G1的Full GC是使用Serial Old来处理的,注意Serial Old会处理整个java堆,包括新生代、老年代、元数据区。
 
G1在执行上述四个步骤时,既处理了新生代,也处理部分老年代,其收集方式也称作混合收集(Mixed GC)。目前只有G1有这种收集方式。
 
 
 

参考资料:

《深入理解Java虚拟机 第3版》
 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值