Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
Serial(串行)收集器是最基本,历史最悠久的垃圾收集器了.大家看名字就知道这个收集器是一个单线程收集器了.它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它再进行垃圾收集工作的时候必须暂停其他所有的工作线程("Stop The World "),直到它收集结束.
新生代采用标记-复制算法,老年代采用标记-整理算法
垃圾收集算法参考:常见的垃圾收集算法
虚拟机的设计者们当然知道Stop The World会带来不良的用户体验,所以在后续的垃圾收集器设计中停顿时间在不断的缩短 (仍然还有停顿,寻找最优秀的垃圾收集器的过程仍在继续).
优点: 与其他收集器的单线程相比,Serial收集器简单而高效,Serial收集器由于没有线程交互的开销,自然可以获得很高的单线程收集效率.
Serial Old收集器是Serial收集器的老年代版本, 它同样是一个单线程收集器.它主要有两大用途:
- 在JDK1.5及以前的版本与Parallel Scavenge收集器搭配使用
- 作为CMS收集器的后备方案
Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
Parallel收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数;收集算法;回收策略等等)和Serial收集器类似.默认的收集线程数跟cpu核数相同,当然也可以用(-XX:ParallelGCThreads)指定收集线程数,但是一般不建议修改
Parallel Scavenge收集器关注的点是吞吐量(高效的利用CPU).CMS等垃圾收集器关注点更多的是STW的时间(提高用户体验).所谓的吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值. Parallel Scavenge收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的同学,可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择.
新生代采用标记-复制算法,老年代采用标记-整理算法
Parallel Old收集器是Parallel Scavenge收集器的老年代版本. 使用多线程和 “标记-整理” 算法.在注重吞吐量以及CPU资源的场合,都可以优先考虑Parallel Scavenge收集器和Parallel Old收集器 (JDK8默认的新生代和老年代收集器).
ParNew收集器(-XX:+UseParNewGC)
ParNew收集器其实跟Parallel收集器很类似, 区别主要在于它可以和CMS收集器配合使用
新生代采用标记-复制算法,老年代采用标记-整理算法
它是许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,只有它能与CMS收集器(真正意义上的并发收集器)配合工作.
CMS收集器(-XX:+UseConcMarkSweepGC(old))
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器.它非常符合在注重用户体验的应用上使用,他是HotSpotu虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作.
从名字中的Mark Sweep这两个词可以看出,CMS收集器是一种 "标记-清除"算法实现的,它的运作过程相比前面几种垃圾收集器来说更加复杂一些.整个过程:
- 初始标记: 暂停所有的其他线程 (STW),并记录下GC Roots直接能引用的对象,速度很快
- 并发标记: 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行.因为用户程序继续运行,可能会有导致已经标记过的对象状态发生了改变
- 重新标记: 重新标记阶段就是为了修正并发标记期间因为用户线程继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始阶段的时间稍长,远远比并发标记阶段时间短,主要用到三色标记里的增量更新算法做重新标记
- 并发清理: 开启用户线程,同时GC线程开始对未标记的区域做清扫,这个阶段如果有新增对象会标记为黑色不做任何处理
-
并发重置: 重置本次GC过程中的标记数据
从它的名字可以看出它是一款优秀的垃圾收集器,主要优点:并发收集;低停顿. 但是它有下面几个明显的缺点:- 对CPU资源敏感(会和服务抢资源)
- 无法处理浮动垃圾(在并发标记和并发清理阶段产生的垃圾,这种垃圾只能等到下一次GC再清理了)
- 它使用的回收算法 "标记-清除"算法会导致收集结束时会有大量空间碎片产生,当然通过参数-XX:+UseCMSCompactAtFullCollection可以让JVM再执行完标记清除后再做整理
- 执行过程中的不确定行,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是再并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发Full GC,也就是"concurrent mode failure"此时会进入"Stop The World",用serial old垃圾收集器来回收.
CMS的相关核心参数
- -XX:+UseConcMarkSweepGC: 启用cms
- -XX:ConcGCThreads: 并发的GC线程数
- -XX:CMSFullGCsBeforeCompaction: 多少次FullGC之后压缩一次,默认是0,代表每次FullGC都会压缩一次
- -XX:+UseCMSCompactAtFullCollection: FullGC之后做压缩整理(减少碎片)
- -XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认92%)
- -XX:+UseCMSInitiatingOccupancyOnly: 只是用设定的回收阈值(XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅再第一次使用设定值,后续则会自动调整
- -XX:+CMSScavengeBeforeRemark: 在CMS GC前启动一次Minor GC,降低CMS GC标记阶段(也会对年轻代一起做标记,如果Minor GC就干掉了很多垃圾对象,标记阶段就会减少一些标记时间)时的开销,一般CMS的GC耗时80%都是在标记阶段
- -XX:+CMSParallellnitialMarkEnabled: 在初始标记的时候多线程执行,缩短STW的时间
- -XX:+CMSParallelRemarkEnabled: 在重新标记的时候多线程执行,缩短STW的时间