垃圾收集器

1、Serial收集器(-XX:+UseSerialGC、-XX:+UseSerialOldGC)
Serial(串行)收集器是最基本、最古老的一款收集器了,从命名上看,它是一个单线程收集器,它的单线程的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程,即stop the world(STW),直到它收集结束。新生代采用复制算法,老年代采用标记-整理算法。
2、Parallel Scavenge收集器(-XX:+UseParallelGC、-XX:+UseParallelOldGC)
Parallel收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等)和Serial收集器类似。默认的收集线程数与CPU核数相同,当然也可以用参数(-XX:+ParallelGCThreads)指定收集线程数,但是一般不推荐修改。Parallel关注的是吞吐量,高效率的利用CPU。所谓吞吐量就是CPU中运行用户代码的时间与CPU总消耗时间的比值。Parallel提供了很多参数供用户去找到最合适的的停顿时间或最大吞吐量。跟Serial收集器一样,Parallel收集器也是新生代采用复制算法,老年代采用-标记-整理算法。

JDK8默认新生代使用Parallel收集器,老年大使用ParallelOld收集器
3、ParNew收集器(-XX:+UseParNewGC)
ParNew收集器其实和Parallel收集器非常类似,唯一的区别是它可以和CMS(Concurrent Mark Sweep)收集器配合使用,只有它和Serial收集器可以和CMS配合使用,所以它是许多运行在Server模式下虚拟机的首选的收集器。

4、CMS收集器(-XX:+UseConcMarkSweepGC)
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验上的应用上使用,它是Hotspot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾回收线程和用户线程同时工作。
从名字中可以看出,它使用的是标记-清除算法,它的运行过程相比上面介绍的收集器更复杂一些。分为四个步骤:
初始标记:暂停所有的其他线程(STW),并记录GC Roots直接能引用的对象,速度很快。
并发标记:它是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长,但不需要暂停用户线程,它们可以并发运行。但因为用户线程不停顿运行,可能会导致已经标记过的对象的状态发生了改变。
重新标记:重新标记就是为了修正并发标记期间因为用户线程不停顿而导致标记产生变动的那一部分对象的标记记录。这个阶段的停顿时间一般会比初始阶段的停顿时间稍长,但远远比并发标记时长短,主要用到三色标记里的增量更新算法做重新标记。
并发清理:开启用户线程,并对GC未标记的区域做清理,这个阶段如果有新的对象产生会被直接标记为黑色对象不做处理(见三色标记详解)
并发重置:重置本次GC过程中的标记数据。

从它的名字可以看出它是一款优秀的垃圾收集器,主要优点是:并发收集,低停顿。但也有明显的缺点:
对CPU资源敏感(和用户线程抢资源);
无法处理浮动垃圾(在并发标记和并发清理阶段又产生了垃圾,只能等下次GC的时候再清理);
使用的回收算法标记-清除会产生大量空间碎片,不过可以使用参数-XX:+UseCMSCompactAtFullCollection可以让JVM在执行完标记清除后再做整理;
执行过程中的不确定性,会存在上一次垃圾回收还未执行完,然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段,一边回收,系统一边运行,有可能会产生并发失败,也许没有回收完就再次出发FullGC,也就是Concurrent Mode Failure的情况,此时会进入STW,当该情况发生时,收集器会自动采用SerialOld收集器进行回收
CMS相关核心参数:
1) -XX:+UseConcMarkSweepGC:启用CMS
2) -XX:ConcGCThreads:并发线程数
3) -XX:+UseCMSCompacetAtFullCollection:FullGC后做压缩整理(减少空间碎片)
4) -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩整理一次,默认为0,意为每次FullGC后都会压缩整理一次
5) -XX:CMSInitiatingOccupancyFraction:当老年代使用达到该比例时会触发FullGC(默认值是92)
6) -XX:+UseCMSCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:+CMSInitiatingOccupancyFraction设定的值),如果不知道,JVM仅在第一次使用默认值,后续会自动调整
7) -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次Minor GC,目的在于减少老年代对年轻代的引用,降低CMS在标记阶段时的开销,一般CMS80%的GC耗时都在标记阶段
8) -XX:+CMSParallelInitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
9) -XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW
垃圾收集底层算法实现
三色标记
在并发标记的过程中,因为标记期间用户线程还在继续跑,对象间的引用可能发生变化,多标和漏标的情况就有可能发生。
这里引入"三色标记",把GCRoots可达性分析遍历对象过程中遇到的对象,按照"是否访问过"这个条件标记成以下三种颜色:
黑色:标识对象已经被垃圾收集器访问过,且这个对象所有的引用都已经被扫描过。黑色对象代表已经被扫描过,它是安全存活的,如果有其他对象引用了黑色对象,无须重新扫描一遍。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
灰色:表示对象已经被垃圾收集器访问过,但这个对象至少存在一个引用还没有被扫描过。
白色:表示对象尚未被垃圾收集器访问过。
显然在可达性分析刚开始的阶段,所有的对象都是白色的,若在分析结束的阶段,仍是白色的,即代表不可达。

多标-浮动垃圾
在并发标记过程中,如果方法运行结束导致部分局部变量(GCRoot)被销毁,但这个GCRoot引用的对象之前又被扫描过(被标记为非垃圾对象),那么本轮GC不会回收这部分内存。这部分本应该回收但没有被回收到的内存,被称为"浮动垃圾"。浮动垃圾并不会影响垃圾回收的正确性,只是需要等到下一轮垃圾回收中才会被清理。

浙公网安备 33010602011771号