如果说垃圾收集算法(标记-清除算法、复制算法、标记-整理算法)是内存回收的方法论,垃圾收集器就是内存回收的具体实现(以下垃圾收集器介绍仅限于HotSpot虚拟机,截止到Java 12)。
串行收集器是最古老、最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。新生代、老年代使用串行回收;垃圾收集的过程中会“Stop The World”(服务暂停)。
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。
ParNew收集器其实就是Serial收集器的多线程版本。除了使用多线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、“Stop The World”、对象分配规则、回收策略等都与Serial收集器完全一样。
Parallel Scavenge收集器是一个新生代收集器,它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,是HotSpot虚拟机真正意义上第一款实现了并发的收集器。从名字上就可以看出CMS收集器是基于标记-清除算法实现的(可以设置碎片收集的周期),收集的范围是老年代。整个过程分为5个步骤:包括:
由于整个过程中耗时最长的并发标记和并发清理过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起“并发”地执行。
但是需要留意的是,如果存在上一次GC还没执行完,然后垃圾收集又被触发的情况,也就是说上一次还没回收完就再次触发GC,特别是在并发标记和并发清理阶段,此时会触发“concurrent mode failure”。在此期间会停止用户线程,用Serial Old收集器来进行回收。
上面提到的垃圾收集器,收集的范围都是整个新生代或者老年代,而G1不再是这样。使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),即Region块。有Eden块、Survivor块、Old块和专门用来存放大对象的Humongous块(当一个对象的存储空间超过一个Region块大小的50%的时候,就会被放入到Humongous块中。如果一个大对象太大,可能会横跨多个Region块来存储)之分。虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。
G1收集器的运作过程与CMS较为相似,前三步是一样的。其过程如下:
值得一说的是,CMS清理阶段是跟用户线程一起并发执行的,而G1因为内部实现太过复杂所以暂时没能实现并发回收。不过到了Shenandoah收集器就实现了并发收集,Shenandoah收集器可以看作是G1的升级版本。
G1相对于CMS另外一个更大的优势,就是可以设置可预测的停顿模型,能够使开发者明确指定在长度为M毫秒的时间片段内,消耗在垃圾收集器上的时间不能超过N毫秒。这个特性使得G1可以适合几十个G的大内存使用场景,因为在那种情况下,如果不加控制,即使是Young GC也要消耗很长的时间。
G1垃圾收集的分类如下,其与一般的GC也不太相同:
需要说明的一点是,从Java 9开始,默认的垃圾收集器已经改为了G1,代替了之前的Parallel Scavenge(新生代) + Parallel Old(老年代)的组合。
ZGC收集器是从Java 11开始支持的具有实验性质的垃圾收集器,其目标如下:
ZGC收集器是一款基于Region内存布局的,暂时不设分代,使用了读屏障、颜色指针等技术来实现可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器。其设计上借鉴了Azul System公司开发的C4(Concurrent Continuously Compacting Collector)收集器。
ZGC中的Region块分为大、中、小三种容量:
ZGC的运作过程如下:
Java 11中还加入了一个比较特殊的垃圾收集器——Epsilon,该垃圾收集器被称为“no-op”收集器,将处理内存分配而不实施任何实际的内存回收机制。 也就是说,这是一款不做垃圾回收的垃圾回收器。这个垃圾回收器看起来并没什么用,主要可以用来进行性能测试、内存压力测试等,Epsilon GC可以作为度量其他垃圾回收器性能的对照组。大神Martijn说,Epsilon GC至少能够帮助理解GC的接口,有助于成就一个更加模块化的JVM。
Java 12中新加入的垃圾收集器,Shenandoah最初的目标是把GC停顿时间降到10毫秒以下,并且对内存的支持扩展到TB级别。为了降低停顿时间,回收器需要使用更多的线程来并行处理回收任务。而要在降低停顿时间的同时能够支持更大的堆空间,回收器对CPU的多核处理能力提出了更高的要求。相比于G1,Shenandoah不仅进行并行的垃圾标记,在压缩堆空间时也是并行进行的。
值得一提的是,作为首个由非Oracle开发,由RedHat领导开发的垃圾收集器,其目标又与Oracle在Java 11中发布的ZGC几乎完全一致,两者天生就存在竞争。于是Oracle马上用实际行动抵制了这个新收集器,在OracleJDK 12里把Shenandoah的代码通过条件编译强行剔除掉(OpenJDK 12中仍然可用)。