原创

JVM实战(3)——理论基础篇: 垃圾回收器

温馨提示:
本文最后更新于 2023年07月10日,已超过 500 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

垃圾回收器(Garbage Collector)是Java虚拟机(JVM)的一个重要组成部分,它负责自动管理内存并回收不再使用的对象。在JVM中,有多种垃圾回收器可供选择,每种回收器都有其特定的工作原理和适用场景。本文将介绍JVM中常见的垃圾回收器,包括Serial GC、ParNew GC、Parallel GC、CMS GC、Serial Old GC、Parallel Old GC、Explicit GC、G1 GC、ZGC和Shenandoah。

一、Serial GC:

Serial GC是最基本的垃圾回收器,采用单线程进行垃圾回收操作。它适用于小型应用和简单的客户端环境,因为其回收效率相对较低。它采用"Stop-the-World"(暂停所有应用线程)的方式进行垃圾回收。它通过标记-复制算法来进行垃圾回收,即先标记出存活对象,然后将存活对象复制到新的内存区域中,最后清理原有的内存空间。

算法:Serial GC使用的是标记-复制算法,它将堆内存分为年轻代和老年代,分别进行垃圾回收。对于年轻代,Serial GC使用的是复制算法,将存活对象复制到一个幸存者区域,并进行对象的年龄标记。对于老年代,Serial GC使用的是标记-整理算法,即标记出存活对象后,将其压缩到内存的一端,清理掉垃圾对象。

配合使用的场景:Serial GC适用于单核处理器或者小型应用场景,对于需要简单且高效的垃圾回收需求的应用程序较为合适。

优点:实现简单,内存占用较低,适用于资源受限的环境。在单线程环境下,没有线程间的竞争,避免了多线程带来的开销。

缺点:垃圾回收过程会暂停所有应用线程,可能导致较长的停顿时间,不适合对停顿时间要求较高的应用场景。回收效率相对较低,适用于小型应用和简单的客户端环境。

JDK引入版本:自JDK 1.3 版本开始引入,是JDK默认的垃圾回收器。

二、ParNew GC:

ParNew GC是Serial GC的并行版本,它使用多线程进行垃圾回收。与Serial GC相比,ParNew GC具有更高的吞吐量,适用于多核处理器和低延迟要求不高的场景。它采用"Stop-the-World"的方式进行垃圾回收操作,但可以通过使用多个线程并行处理垃圾回收任务,提高回收效率。

算法:ParNew GC的算法和Serial GC类似,使用的是标记-复制和标记-整理算法。不同之处在于,ParNew GC使用多个线程来并行处理垃圾回收任务,提高了回收效率。

配合使用的场景:ParNew GC适用于多核处理器环境下的应用程序,对于需要高吞吐量的场景,但对于停顿时间要求不是特别苛刻的应用。

优点:相比于Serial GC,ParNew GC可以利用多线程并行处理垃圾回收任务,提高回收效率。适用于多核处理器环境下的应用程序。

缺点:停顿时间较长,仍然存在"Stop-the-World"暂停所有应用线程的问题,不适用于对停顿时间要求较高的应用场景。

JDK引入版本:自JDK 1.3 版本开始引入,是Serial GC的并行版本。

三、 Parallel GC:

Parallel GC是一种使用多线程进行垃圾回收的回收器,它主要关注系统吞吐量的最大化。与ParNew GC类似,它使用多线程并行处理垃圾回收任务。它适用于需要高吞吐量的应用,但相对而言可能会导致较长的停顿时间。

算法:Parallel GC使用的是标记-复制和标记-整理算法,类似于Serial GC和ParNew GC。它将堆内存划分为多个区域,并使用多个线程并行处理垃圾回收任务。

配合使用的场景:Parallel GC适用于对系统吞吐量要求较高的应用程序,适合在具有多核处理器且有一定空闲时间的情况下使用。

优点:并行处理垃圾回收任务,提高了回收效率和系统吞吐量。适用于需要高吞吐量的应用场景。

缺点:由于并行处理的特性,可能导致较长的停顿时间,不适用于对停顿时间要求较高的应用场景。在大规模应用和低延迟要求的场景中,性能可能不如其他回收器。

JDK引入版本:自JDK 1.4 版本开始引入,是JDK中的一种并行垃圾回收器。

 

四、CMS GC:

CMS(Concurrent Mark Sweep)GC是一种以最短停顿时间为目标的垃圾回收器。它通过并发标记和并发清除阶段减少应用程序的停顿时间。CMS GC适用于对停顿时间要求较高的中型应用。

算法:CMS GC使用的是标记-清除算法。它通过并发标记阶段来标记出存活对象,然后在并发清除阶段对未标记的对象进行清除。

配合使用的场景:CMS GC适用于对停顿时间要求较高的中型应用场景。它通过并发标记和并发清除阶段减少了垃圾回收过程中应用程序的停顿时间。

优点:相比于Serial GC和Parallel GC,CMS GC的停顿时间较短,适用于对停顿时间要求较高的应用场景。

缺点:CMS GC使用的是标记-清除算法,可能导致内存碎片问题。并发执行垃圾回收任务会占用一部分应用程序的处理能力,可能会影响应用程序的吞吐量。

JDK引入版本:自JDK 1.4 版本开始引入,是一种以最短停顿时间为目标的并发垃圾回收器。

 

五、Serial Old GC:

Serial Old GC是Serial GC的老年代版本,用于回收老年代的对象。它采用单线程进行垃圾回收,适用于较小规模的应用。

算法:Serial Old GC使用的是标记-整理算法。它标记出存活对象后,将其压缩到内存的一端,清理掉垃圾对象。

配合使用的场景:Serial Old GC适用于较小规模的应用,对于需要简单且高效的垃圾回收需求的应用程序较为合适。

优点:与Serial GC类似,Serial Old GC实现简单,适用于资源受限的环境。

缺点:垃圾回收过程会暂停所有应用线程,可能导致较长的停顿时间。回收效率相对较低,适用于较小规模的应用。

JDK引入版本:自JDK 1.4 版本开始引入,是Serial GC的老年代版本。

 

六、Parallel Old GC:

Parallel Old GC是Parallel GC的老年代版本,也是一种并行垃圾回收器。它适用于大型、多核处理器上的应用,通过多线程提高吞吐量。

算法:Parallel Old GC使用的是标记-整理算法,类似于Serial Old GC。它将堆内存划分为多个区域,并使用多个线程并行处理垃圾回收任务。

配合使用的场景:Parallel Old GC适用于大型、多核处理器上的应用程序,通过多线程提高吞吐量。

优点:相比于Serial Old GC,Parallel Old GC可以利用多线程并行处理垃圾回收任务,提高回收效率。适用于多核处理器环境下的应用程序。

缺点:与Parallel GC类似,可能导致较长的停顿时间,不适用于对停顿时间要求较高的应用场景。

JDK引入版本:自JDK 5.0 版本开始引入,是Parallel GC的老年代版本。

 

七、Explicit GC:

Explicit GC是一种手动触发的垃圾回收方式。开发人员可以通过代码显式地调用System.gc()方法来触发垃圾回收操作。然而,建议仅在特殊情况下使用显式GC调用,因为JVM通常可以自动管理垃圾回收。

配合使用的场景:Explicit GC主要用于特殊情况下的垃圾回收需求。通常情况下,JVM可以自动管理垃圾回收,不需要显式地调用System.gc()方法。

优点:可以在特殊情况下手动控制垃圾回收操作。

缺点:滥用显式GC调用可能会导致不必要的性能开销,并且可能无法达到预期的效果。通常情况下,应该依赖JVM自动管理垃圾回收。

JDK引入版本:从JDK的早期版本开始就支持,开发人员可以通过System.gc()方法显式触发垃圾回收。

 

八、G1 GC:

G1(Garbage First)GC是一种面向服务器应用的垃圾回收器,它通过将堆内存划分为多个大小相等的区域(Region)来管理内存。G1 GC的目标是在可控制的停顿时间内实现高吞吐量。

算法:G1 GC使用的是分代、并发、增量的标记-整理算法。它将堆内存划分为多个区域,并在每个区域中执行垃圾回收任务。

配合使用的场景:G1 GC适用于需要高吞吐量且对停顿时间要求较高的应用程序。它通过分代、并发和增量的特性来平衡垃圾回收的效率和停顿时间。

优点:相比于CMS GC,G1 GC可以更好地控制停顿时间,并且可以处理较大堆内存。适用于需要高吞吐量和低停顿时间的应用场景。

缺点:G1 GC在处理大规模堆内存时可能会增加一些开销,适合中大规模应用场景。

JDK引入版本:自JDK 7 版本开始引入,是一种面向服务器应用的垃圾回收器。

 

九、ZGC:

ZGC是一种低延迟的垃圾回收器,它专注于减少应用程序的停顿时间。它使用了一种基于读屏障的并发压缩算法,并通过可并发的标记和整理阶段来实现快速垃圾回收。

配合使用的场景:ZGC适用于对低延迟和大堆内存需求的应用。它的主要目标是最小化停顿时间,适合对停顿时间要求极高的场景。

优点:ZGC可以在毫秒级别的停顿时间内执行垃圾回收,对于对低延迟要求非常高的应用程序非常有用。

缺点:与其他垃圾回收器相比,ZGC可能会有一些性能开销,适合对低停顿时间要求较高的场景。

JDK引入版本:自JDK 11 版本开始引入,是一种低延迟的垃圾回收器。

 

十、Shenandoah:

Shenandoah垃圾回收器是一种低停顿时间的垃圾回收器,它旨在减少Java应用程序的停顿时间,并提供高吞吐量的垃圾回收。下面是对Shenandoah回收器的详细说明:

1. 工作原理:

Shenandoah使用了一种被称为"concurrent evacuation"的并发标记-整理算法。它通过并发执行垃圾回收的各个阶段来最小化应用程序的停顿时间。在标记阶段,Shenandoah使用了一种读屏障技术来跟踪对象的引用关系,并标记出存活的对象。在整理阶段,它会将存活的对象移动到堆的一端,从而产生连续的内存空间。

2. 特点:

   - 低停顿时间:Shenandoah通过并发执行垃圾回收的不同阶段,最大限度地减少了应用程序的停顿时间。它使用了一种增量更新引用的机制,以便在并发执行的同时保持引用的一致性。

   - 高吞吐量:尽管Shenandoah的主要目标是降低停顿时间,但它也提供了良好的吞吐量性能,使其适用于需要高吞吐量的应用场景。

   - 可伸缩性:Shenandoah能够有效地利用多核处理器和内存,并通过并发执行来提高回收的效率。

3. 适用场景:

Shenandoah适用于对低停顿时间和高吞吐量有要求的应用程序。特别是对于那些需要快速响应和低延迟的应用场景,例如实时系统、大规模服务和内存敏感型应用。

4. JDK版本支持:

Shenandoah垃圾回收器首次引入于JDK 12版本,并在后续的JDK版本中得到改进和优化。在使用Shenandoah之前,请确保你正在使用支持该垃圾回收器的JDK版本,并参考官方文档了解具体的支持情况。

总而言之,Shenandoah是一种注重低停顿时间和高吞吐量的垃圾回收器,通过并发执行不同阶段的垃圾回收任务来实现这一目标。它在实时性要求高、对停顿时间敏感的应用场景中表现出色,并且适用于需要高吞吐量的大型应用程序。详细的使用说明和配置参数可以在相应JDK版本的官方文档中找到。

如下图所示,虚线之上为年轻代垃圾回收器,虚线之下为老年代垃圾回收器。而从G1开始,已经没有物理上的分代划分了(G1逻辑上还有分代,之后的GC逻辑上也没有划分)

JVM的垃圾回收器提供了多种选择,每种回收器都有其优势和适用场景。开发人员应根据应用程序的特性和需求选择合适的垃圾回收器,以实现最佳的性能和内存管理效果。

关于Serial GC、Parallel GC和CMS的详细对比说明,可以参考官方的《JVM内存管理白皮书》,这里不再进行赘述。

至于G1、ZGC相关的内容,在官方文档《HotSpot Virtual Machine Garbage Collection Tuning Guide》也可以找到。

正文到此结束