老年代收集器

上一篇介绍了三种新生代垃圾收集器。这一篇主要介绍三种老年代垃圾收集器。

Serial Old(-XX:+UseSerialGC)

Serial Old 是 Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。此收集器的主要意义也是在于给Client模式下的虚拟机使用。如果在Server模式下,它还有两大用途:

  • 在JDK1.5 以及之前版本(Parallel Old诞生以前)中与Parallel Scavenge收集器搭配使用。
  • 作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

它的工作流程与Serial收集器相同

Parallel Old / Parallel MSC(-XX:+UseParallelOldGC)

Parallel Old收集器是Parallel Scavenge的老年代版本,使用多线程的标记-整理算法,Parallel Old收集器在JDK1.6才开始提供。在JDK1.6之前,新生代使用Parallel Scavenge收集器只能搭配老年代的serial old收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量。在JDK1.6之前(Parallel Scavenge+Serial old)。Serial old正是为了在老年代同样吞吐量优先的垃圾回收器,如果系统对吞吐量要求比较高,JDK1.8后可以优先考虑新生代Parallel Scavenge 和老年代Parallel Old收集器的搭配策略。

Parallel Scavenge 回收器同样考虑了吞吐量优先这一指标,非常适合那些注重吞吐量和CPU资源敏感的场合。

CMS(-XX:+UseConcMarkSweepGC)

  1. 收集器是一种以**获取最短回收停顿时间**为目标的收集器,它非常符合那些集中在互联网站或者B/S系统的服务端上的Java应用,CMS非常适合堆内存大,CPU核数多的服务器端应用,也是G1出现之前大型应用的首选收集器。这些应用都非常重视服务的响应速度。从名字上(“Mark Sweep”)就可以看出它是基于**“标记-清除”**算法实现的。

对应的JVM参数:-XX:+UseConcMarkSweepGC,开启该参数后会自动将-XX:+UseParNewGC打开。 开启该参数后,使用ParNew(新生代区)+CMS(Old区)+Serial(Old)出错后备项组合。

CMS收集器工作的整个流程分为以下4个步骤:

  • 初始标记(CMS initial mark):仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,需要“Stop The World”。
  • 并发标记(CMS concurrent mark):进行GC Roots Tracing的过程,在整个过程中耗时最长。
  • 重新标记(CMS remark):为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。此阶段也需要“Stop The World”。
  • 并发清除(CMS concurrent sweep)

由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
WX20191215-212610@2x.png

CMS优点:

并发收集低停顿,因此CMS收集器也被称为并发低停顿收集器(Concurrent Low Pause Collector)。

CMS缺点:

  1. 对CPU资源非常敏感

    面向并发设计的程序都对CPU资源比较敏感。在并发阶段,它虽然不会导致用户线程停顿,但会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量+3)/4,也就是当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU资源,并且随着CPU数量的增加而下降。但是当CPU不足4个时(比如2个),CMS对用户程序的影响就可能变得很大,如果本来CPU负载就比较大,还要分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然降低了50%,其实也让人无法接受。

  2. 无法处理浮动垃圾(Floating Garbage)

    可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生。这一部分垃圾出现在标记过程之后,CMS无法再当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就被称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。

  3. 标记-清除算法导致的空间碎片

    CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象。CMS 配合适当内存整理策略,在一定程度上可以解决这个问题。