以下讨论收集器基于JDK1.7Update4,虚拟机包含所有收集器如图
收集器之间存在连线,说明他们可以搭配使用,虚拟机所处的区域,表示它是属于新生代或老年代收集器。
吞吐量:就是CPU用于运行用户代码的事件与CPU总消耗时间的比值,即 吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),如 JVM总共运行100min,其中垃圾收集用掉 1min,那吞吐量就是99%;
并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态;
并发(Concurrent):指用户线程与垃圾收集线程同时执行(也可能交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。
1.Serial收集器
该收集器是最基本,该收集器是一个单线程收集器,对于运行在Client模式下的虚拟机是一个很好的选择。
以下为收集器运行示意图
2.ParNew收集器
parNew其实就是Serial收集器的多线程版本。目前只有它能与CMS收集器配合工作,可以使用-XX:+UseConcMarkSweepGC选项后默认新生代收集器就是ParNew,或者使用-XX:+UseParNewGC强制指定,可以使用-XX:ParallelGCThreads参数限制垃圾收集线程数。
以下为收集器运行示意图
3.Paraller Scavenge收集器(吞吐量优先)
是一个新生代收集器,是使用复制算法的收集器,又是并行的多线程收集器;
该收集器目标是达到一个可控制的吞吐量(Throughput)。
高吞吐量好处:可以高效地利用CPU时间,尽快完成程序运算任务,主要适合在后台运算而不需要太多交互的任务。
控制最大垃圾收集停顿时间:-XX:MaxGCPauseMillis,该参数允许的值是一个大于 0的毫秒数,收集器将尽可能保证内存回收时间不超过设定的值
设置吞吐量大小的-XX:GCTimeRatio,该参数值为一个大于 0 且小于 100 的整数,也就是垃圾收集时间占总时间的比率,相当于吞吐量的倒数
-XX:+UseAdaptiveSizePolicy,这是个开关,代开后,就需要手工指定新生代的大小(-Xmn)、Eden与Survivor的比例(-XX:SurvivorRatio)、晋升老年代对象大小(-XX:PretenureSizeThresHold)等细节参数,JVM会根据当前系统的运行情况收集性能控制信息,动态调整这些参数以提供最合适的停顿时间或是最大吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)
4.Serial Old收集器
是Serial收集器的老年代版本,是一个单线程收集器,使用"标记-整理"算法。
以下为收集器运行示意图
5.Parallel Old收集器
是Parallel Scavenge收集器的老年代版本,使用"标记-整理"算法
在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge + Parallel Old收集器。
以下为收集器运行示意图
6.CMS(Concurrent Mark Sweep)收集器(并发低停顿收集器)
是一种以获取最短回收停顿时间为目标的收集器,基于"标记-清除"算法实现
运作过程分为4步:
6.1.初始标记(CMS initial mark),需要"Stop The World",只是标记下GC Roots能直接关联到的对象,速度很快;
6.2.并发标记(CMS concurrent mark),进行 GC Roots Tracing的过程;耗时最长,可以和用户线程一起并发执行;
6.3.重新标记(CMS remark),需要"Stop The World",为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,时间稍长一些;
6.4.并发清除(CMS concurrent sweep),耗时最长,可以和用户线程一起并发执行;
总体上,CMS收集器的内存回收过程是与用户线程一起并发执行的;
优点:并发收集、低停顿
缺点:
6.2.1.CMS默认启动的回收线程数是(CPU数量 + 3) / 4,当CPU不足4个时对用户程序的影响可能会变大;
6.2.2.CMS无法处理浮动垃圾(Floating Garbage),由于CMS并发清理阶段用户线程还在运行,伴随程序运行自然就还会有新的垃圾产生,这部分垃圾出现在标记过程之后,CNS无法再档次收集中处理他们,只好留待下次GC清理,这部分垃圾称为"浮动垃圾",也是由于在垃圾收集阶段用户线程还需要运行,也就需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等老年代几乎被填满在进行收集,需要预留一部分空间提供并发收集是程序运行时使用,可以用-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比
如果CMS运行期间预留的内存空间无法满足程序需要,就会出现一次"Concurrent Mode Failure"失败,这时JVM会临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就长了,所以-XX:CMSInitiatingOccupancyFraction不宜设置太高
6.2.3.CMS基于“标记-清除”这意味着收集结束就会产生大量空间碎片,为了解决这个问题
CMS提供了一个-XX:+UsrCMSCompactAtFullCollection开关(默认开启),用于在CMS要进行Full GC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的,停顿时间会变成;
另外-XX:CMSFullGCsBeforeCompaction参数用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值0,表示每次进入Full GC是都进行碎片整理)。
以下为CMS收集器运行示意图
7.G1(Garbage-First)收集器
是一款棉线服务端应用的垃圾收集器,使命是替换掉CMS收集器,有如下特点:
7.1.并行与并发
7.2.分代收集
7.3.空间整合:G1从整体来看是基于"标记-整理"算法实现的收集器,从局部看是基于"复制"算法实现的,G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存;
7.4.可预测的停顿:使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎是实时Java(RTSJ)的垃圾收集器的特征了
G1运作分为以下步骤:
7.2.1.初始标记(Initial Marking):要停顿线程,但耗时短,标记GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下阶段用户程序并发运行时,能在正确可用的Region中创建对象;
7.2.2.并发标记(Concurrent Marking):从GC Roots开始对堆中对象进行可达性分析,找出存活的对象,该阶段耗时较长;
7.2.3.最终标记(Final Marking):需要停顿线程,但可并行执行,为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,Remembered Set Logs的数据合并到Remembered Set中
7.2.4.筛选回收(Live Data Counting and Evacuation):首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,需要停顿线程,但可并发执行
下图为G1收集器运行示意图