前言
在本系列的前两篇文章,已经讲过了内存的垃圾回收方法以及内存的分代模型,但真正把它们整合起来,做具体垃圾回收工作的,还是垃圾回收器
。
随着工业界内存的不断扩大,垃圾回收器也已经更新了好几个世代。
本文就会对市面上常见的几个垃圾回收器进行介绍,
常用的垃圾回收器介绍
首先来看这张图
图中一共有八种垃圾回收器。
除了G1 和 ZGC以外,其他的垃圾回收器都是基于内存分代模型的。
有些垃圾回收器是在YGC(Young Garbage Collection)中被触发,而有一些垃圾回收器是在FGC(Full Garbage Collection)的时候被触发。
这样就会出现两种垃圾回收器组合使用的情况。
下面就会先介绍常用的垃圾回收器组合。
Serial + Serial Old
Serial与Serial Old垃圾回收器在JDK诞生之初就追随出现了。
其工作流程如图所示:
首先需要明确的一点是,垃圾回收工作需要单独起一个线程。
Serial和Serial Old除了在工作区域上不同以外,其他工作机制是相同的——
都是在进行垃圾回收的时候,将所有的工作线程暂停,创建一个单独的垃圾回收线程进行垃圾清理。
将工作线程暂停有个专用名词,叫STW(Stop The World),所有的工作线程为垃圾回收线程让路。
同时Serial Old底层是使用mark-compact
算法进行内存回收。
Parallel Scavenge + Parallel Old
在JDK 1.8之前, JVM就默认使用这两种垃圾回收器的组合。
Serial 和 Serial Old垃圾回收器的弊端肉眼可见,单线程的垃圾回收效率低下。
所以Parallel Scavenge + Parallel Old的组合将单线程改成了多线程,不过仍然需要STW。
工作流程如图所示:
注意,Paralle Old使用了本系列第一篇文章中介绍的mark-compact
垃圾回收算法。
ParNew + CMS
看起来Parallel Scavenge + Parallel Old的组合已经很完美了,但还是有明显可以改进的地方,比如,每次垃圾回收的STW,明显会拖慢JVM的运行速度。
为了解决垃圾回收过程的STW,CMS应运而生。
CMS,全称Concurrent Mark Sweep
。这是一种可以让垃圾回收线程与工作线程同时运行的技术。
而ParNew只是Parallel Scavenge的一种改进版本,为了更好的和CMS进行协同运作。
下面我们来仔细讲讲这个CMS。
CMS
CMS一次运行周期有四个阶段,分别是初始标记
、并发标记
、重新标记
、并发清理
。
用图来表示就像下图这样:
需要指出的是,CMS并没有完全消除掉STW,分别在初始标记阶段和重新标记阶段还会发生STW,不过相比于Parallel Old,CMS已经将最耗时的操作与工作线程并发执行了,从而大大缩短了STW的时间。
下面我们来讲讲一下每一步都具体干了些什么:
初始标记: CMS会把所有的根对象都标记出来,由于只标记根对象,所有STW的时间非常短。
并发标记:这一步是与工作线程并发执行。从上一步标记出来的根对象开始,标记出整个程序的对象图。这个过程虽然耗时较长,但由于是与工作线程并发执行的,并不会造成程序卡顿的现象。
不过并发执行描绘出的对象图谱在运行过程中可能会被更改,比如已经标记为垃圾的对象又重新成为存活对象,或者已经标记为存活对象的又变成了垃圾对象。
因此就需要下一阶段的工作。
重新标记:这一阶段需要进行简单的扫描,来修正那些在并发标记过程中,由于后续程序运行导致标记发生变化的对象。这一阶段需要的STW的时间远远小于并发标记的时间,所以对程序整体的运行影响不大。
并发清理:这一步是将上面几步发现的垃圾对象进行清理。同样是与工作线程并发执行的。
通过将耗时的垃圾标记算法与工作线程并发执行,达到了降低整体STW的时间,就是CMS的思想核心。
不过CMS也有其问题,这也是为什么CMS从来也没有成为JVM默认垃圾回收器的原因。
CMS的问题
首先会产生浮动垃圾的问题,在并发清理的阶段,在一边清理的过程中,又有了新垃圾的产生,不过这些浮动垃圾在下一次CMS运行周期会被处理掉。
另一个更大的问题就是,由于CMS内部是使用了mark-sweep
这种垃圾清除算法,所以mark-sweep带来的内存碎片化问题同样困扰着CMS。
所以当CMS造成的内存碎片化问题严重到没法分配新对象的时候,就会转换成Serial Old垃圾回收器对整个内存进行回收操作。
这就造成了一个问题,CMS在正常的情况下运行速度会比较理想,但一点出现问题,就会造成整个系统非常长时间的卡顿。
总结
本文主要讲了一些常用的垃圾回收器及其常用组合的特性,包括单线程的Serial + Serial Old,多线程的Parallel Scavenge + Parallel Old,还有可以让垃圾回收线程与工作线程并发执行的ParNew + CMS。
而关于最新的G1垃圾回收器,我们会在之后的文章中进行讲解。