1.Serial收集器
Serial收集器是最基本、发展历史最悠久的收集器,这个收集器是一个单线程的收集器,在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。它是虚拟机运行在client模式下的默认新生代收集器。
2.ParNew收集器
ParNew收集器起始就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为完全一样。它是许多运行在server模式下的虚拟机中首选的新生代收集器,其中有一个很重要的原因是,除了Serial收集器,只有它能与CMS收集器配合工作。
3.Parallel Scavenge收集器
CMS等收集器的关注点是尽可能的缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率的利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
4.Serial Old收集器
Serial Old收集器时Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。
5.Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
6.CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,从名字上就可以看出CMS收集器是基于“标记-清除”算法实现的,它的运作过程分为四个步骤:
- 初始标记
- 并发标记
- 重新标记
- 并发清除
其中初始标记和重新标记这两个步骤仍然需要STW(Stop The World)。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
CMS收集器的主要优点已经在名字上体现出来了,并发收集、低停顿,它有以下三个明显的缺点:
- CMS收集器对CPU资源非常敏感,在并发阶段它虽然不会导致用户线程停顿,但是会因为抢占一部分CPU资源而导致应用程序变慢
- CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure”而导致另一次Full GC的产生。
由于CMS并发清理阶段用户线程还在运行,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉他们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段,用户线程还在运行,所以不能等到老年代几乎被填满了再收集,需要预留一部分空间提供并发收集时的程序运作使用。如果CMS运行期间预留的内存无法满足程序所需,就会出现一次“Concurrent Mode Failure”,这是虚拟机将会启动应急预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了 - CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量的内存碎片产生,将会给大对象分配带来麻烦,往往老年代空间还有很大剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数(默认开启),用于即将要进行Full GC时开启内存碎片的合并整理过程,空间碎片问题没有了,但停顿时间不得不变长;虚拟机设计者还提供了一个-XX:CMSFullGCsBeforeCompaction,这个参数用于设置执行多少次不压缩的FullGC后来进行一次带压缩的(默认值为0)。
7.G1收集器
G1(Garbage First)收集器是当今收集器技术发展的最前沿成果之一,与其他GC收集器相比,G1具备如下特点:
- 并行与并发:G1能充分利用多CPU的硬件优势,来缩短Stop-The-World停顿的时间,原本需要停顿Java线程执行的GC动作,G1收集器也可以通过并发的方式让Java程序继续执行
- 分代收集:分代概念在G1中依然得以保留,虽然G1可以独立管理整个GC堆,但它能够采用不同的方式去处理年轻代和老年代。
- 空间整合:与CMS的标记-清理算法不同,G1收集器从整体来看是基于标记-整理算法实现的收集器,从局部(两个region之间)上来看是基于复制算法实现的,意味着G1运作期间不会有内存碎片产生
- 可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒
在G1之前的其他收集器都是回收整个新生代和老年代,使用G1收集器时,它将整个Java堆划分为多个大小相等的独立区域(region),虽然还保留新生代和老年代的概念,但新生代和老年代不再物理隔离,都是一部分region的集合。
G1之所以能建立可预测的停顿时间模型,是因为G1跟踪各个region里面的垃圾堆积的价值大小(回收所获得空间大小/回收所需时间),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的region(这就是Garbage-First名称的由来)。
在G1收集器中,region之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中每个region都有一个与之对应的Remembered Set,虚拟机发现程序在对reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查引用的对象是否处于不同的region之中,如果是就通过CardTable把相关引用信息记录到被引用的对象所属的region的RememberSet之中,当进行内存回收时,在GC根节点的枚举范围内加入RememberedSet,避免对全堆扫描。
G1收集器的运作大致分为以下几个步骤:
- 初始标记
- 并发标记
- 最终标记
- 筛选回收
8.垃圾收集常用参数总结
- UseSerialGC:使用Serial + Serial Old的收集器组合进行内存回收
- UseParNewGC:使用ParNew + Serial Old的收集器组合进行内存回收
- UseConcMarkSweepGC:使用ParNew + CMS + Serial Old收集器进行组合回收。Serial Old收集器作为CMS收集器出现Concurrent Mode Failure失败后的后备收集器
- UseParallelGC:使用Parallel Scavenge + Serial Old的收集器组合进行内存回收
- UseParallel Old GC:使用Parallel Scavenge + Parallel Old的收集器组合进行内存回收
- SurvivorRatio:新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Survivor = 8 : 1
- PretenureSizeThreshold:直接晋升到老年代的对象大小,大于这个参数的对象将直接在老年代分配
- MaxTenuringThreshold:晋升到老年代的对象年龄,每个对象在坚持过一次MinorGC之后,年龄就增加1,当超过这个参数值时就进入老年代
- UseAdaptiveSizePolicy:动态调整Java堆中各个区域的大小以及进入老年代的年龄
- HandlePromotionFailure:是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个eden和survivor区的所有对象都存活的极端情况
- ParallelGCThreads:设置并行GC时进行回收的线程数
- GCTimeRatio:GC时间占总时间的比值,默认值为99,即允许1%的GC时间,仅在使用Parallel Scavenge收集器时生效
- MaxGCPauseMillis:设置GC的最大停顿时间,仅在使用Parallel Scavenge收集器时生效
- CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发垃圾收集,默认值为68%,仅在使用CMS收集器时生效
- UseCMSCompactAtFullCollection:设置CMS收集器在完成FullGC后是否要进行一次内存碎片整理,仅在使用CMS收集器时生效
- CMSFullGCsBeforeCompaction:设置CMS收集器在若干次FullGC后再启动一次内存碎片整理,仅在使用CMS收集器时有效