一文彻底搞懂八种JVM垃圾回收器

1. 垃圾回收器总览

目前主流的垃圾回收器有8种。
在这里插入图片描述

  • 新生代可以适用的垃圾回收器:Serial(串行收集器)、ParNew(并行年轻代收集器)、Parallel Scavenge(并行回收器)
  • 老年代可以适用的垃圾回收器:CMS(并发标记-清除收集器)、Serial Old(串行老年代收集器)、Parallel Old(并行老年代收集器)
  • G1(Garbage First 收集器)和 ZGC(Z Garbage Collector)回收器适用于新生代和老年代混合回收
  • 相互之间有连线的表示可以配合使用,常用组合:Serial+Serial Old, Parallel Scavenge+Parallel Old,ParNew+CMS,G1(不需要组合其他收集器)

名词解释:

  • 并行回收(Parallel):多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
  • 串行回收:单个垃圾收集线程工作
  • Minor GC(Young GC):发生在年轻代的GC
  • Full GC:发生在老年代的GC,full GC回收速度一般比Minor GC慢10倍左右,JVM调优也是主要针对Full GC进行调优

2. Serial 和Serial Old收集器

Serial 系列的垃圾收集器是 Java 虚拟机中最早的一批收集器之一。它们的设计初衷是为了适应早期的硬件环境和应用场景。在那个时候,硬件配置相对较低,主要特点包括内存容量较小、CPU 单核、并发应用场景相对较少。基于这些限制条件,Serial 系列的垃圾收集器采用了简单高效、资源消耗最少、单线程收集的设计思路。

  • 简单高效:由于硬件资源有限,垃圾回收器需要设计得简单高效,以减少系统资源的占用。Serial 系列的垃圾收集器实现简单,适用于小型应用或者简单的测试场景。

  • 资源消耗最少:考虑到当时硬件资源有限,Serial 系列的垃圾收集器尽可能地减少了对系统资源的占用。通过使用单线程执行垃圾回收操作,避免了多线程切换的开销,从而最大程度地节约了系统资源。

  • 单线程收集:由于早期的硬件环境和应用场景下,并发需求较低,采用单线程收集的设计方案足以满足当时的需求。单线程收集简化了垃圾回收器的实现,并降低了系统复杂性,使得垃圾回收过程更加可控和稳定。

垃圾收集流程

Serial 垃圾收集过程的简单之处在于其采用了单线程执行的方式,以简化实现并减少资源占用。

  • 暂停用户线程(Stop the World):
    在开始垃圾收集过程之前,Serial 垃圾收集器会暂停(停止)所有的用户线程。这是为了确保在垃圾收集过程中对象的状态不会被修改,从而保证垃圾收集的准确性。
  • 执行垃圾收集:
    一旦用户线程暂停,Serial 垃圾收集器会开启一个单线程来执行垃圾回收操作。这个线程会遍历堆中的对象,标记并清理不再使用的对象,以释放内存空间。
  • 等待垃圾收集完成:
    在垃圾收集过程中,用户线程会被暂停,直到垃圾收集完毕。这意味着用户线程无法在垃圾收集期间执行任何操作。
    恢复用户线程:

当垃圾收集完成后,Serial 垃圾收集器会恢复用户线程的执行。此时,垃圾已被清理,堆内存中有更多的可用空间供应用程序使用。

注意:“暂停用户线程”,这里也是各种垃圾收集器的一个区分指标,后面的有些垃圾收集器收集的某些阶段是不需要暂停用户线程的。

注:
收集区域: Serial (新生代),Serial Old(老年代)
使用算法: Serial (标记复制法),Serial Old(标记整理法)
搜集方式: 单线程收集

3. Parallel Scavenge 和 Parallel Old收集器

随着硬件资源的升级,包括内存空间的增大和 CPU 的多核化,传统的 Serial 垃圾收集器面临着性能瓶颈。由于它采用单线程执行垃圾回收操作,无法充分利用多核 CPU 的优势,导致在处理大内存空间时性能下降,垃圾回收时间变得更长。为了充分发挥多核 CPU 的优势,JVM 推出了 Parallel 收集器系列。Parallel 收集器的设计思想是利用多线程并行执行垃圾回收操作,以提高整个垃圾收集过程的并行度和性能。

Parallel 收集器的核心特点包括

  • 多线程并行执行:Parallel 收集器利用了多核 CPU 的优势,通过多个线程同时执行垃圾回收操作,加快了垃圾收集的速度。

  • 高吞吐量:由于并行执行垃圾收集操作,Parallel 收集器适用于吞吐量要求较高的应用场景。它能够在保证吞吐量的同时,尽可能地减少垃圾收集的停顿时间。

  • 适用于大内存堆:随着内存空间的扩大,Parallel 收集器能够更好地应对大内存堆的情况,通过并行执行垃圾收集操作,提高了整个垃圾收集过程的效率。

相比于传统的 Serial 收集器,Parallel 收集器能更好地适应现代应用的需求,特别是大型内存堆和高吞吐量的场景。

垃圾收集流程

Parallel Scavenge 和 Parallel Old 是 Parallel 收集器系列的两个组成部分,它们的工作机制相似,都是利用多线程并行执行垃圾回收操作,以提高整个垃圾收集过程的效率和吞吐量。以下以 Parallel Scavenge 为例来说明其工作机制:

  • 多线程并行执行:Parallel Scavenge 收集器利用了多个线程并行执行新生代的垃圾回收操作。这意味着在进行新生代垃圾回收时,多个线程同时工作,加快了垃圾收集的速度。
  • 暂停用户线程:与 Serial 收集器类似,Parallel Scavenge 在进行垃圾收集时会暂停用户线程,以确保垃圾回收的准确性。这一阶段通常称为“Stop the World”。
  • 多线程并发清理:Parallel Scavenge 收集器的特点之一是在新生代垃圾收集过程中采用并行清理(Parallel Cleaning)的方式。这意味着在暂停用户线程期间,多个线程同时清理新生代中的垃圾对象,从而更快地完成垃圾收集过程。
    高效利用多核 CPU:

通过利用多个线程并行执行垃圾收集操作,Parallel Scavenge 能够充分发挥多核 CPU 的优势,提高了垃圾收集的效率。相比于 Serial 收集器,它能更快地完成垃圾回收操作,从而减少了应用程序的停顿时间,从而提高了整个应用程序的性能。

注:
收集区域: Parallel Scavenge (新生代),Parallel Old(老年代)
使用算法: Parallel Scavenge (标记复制法),Parallel Old(标记整理法)
搜集方式: 多线程

4. ParNew 收集器

ParNew 和 Parallel Scavenge 垃圾收集器在实现上确实有一些相似之处,都属于并行垃圾收集器。但 ParNew 垃圾收集器之所以出名,一个重要原因是它是唯一能与 CMS(Concurrent Mark-Sweep)收集器配合使用的新生代收集器,特别适用于那些对停顿时间要求较高的应用场景。

以下是 ParNew 垃圾收集器的一些特点和与 CMS 配合的优势

  • 与 CMS 配合:ParNew 垃圾收集器能够与 CMS 垃圾收集器配合使用,用于处理老年代的垃圾回收。在这种组合中,ParNew 负责新生代的垃圾收集,而 CMS 负责老年代的并发垃圾收集。这种分工合作可以有效地减少应用程序的停顿时间,满足对低停顿时间的需求。
  • 并行收集:ParNew 垃圾收集器采用多线程并行收集的方式,类似于 Parallel Scavenge 收集器。它能够充分利用多核 CPU 的优势,加快垃圾收集的速度,提高整个应用程序的性能。
  • 应对停顿时间要求高的场景:由于 ParNew 与 CMS 配合使用,可以针对那些对停顿时间要求较高的应用场景。CMS 收集器通过并发执行垃圾回收操作,尽量减少停顿时间,而 ParNew 则能够在新生代中高效地执行垃圾回收操作,进一步降低停顿时间。

垃圾收集流程
ParNew 收集器和 Parallel Scavenge 收集器在工作流程上确实非常相似,都是并行垃圾收集器。

  • 停止应用程序线程(Stop the World):在进行垃圾收集之前,ParNew 收集器会暂停所有的用户线程。这一阶段被称为停止应用程序线程,以确保在垃圾收集过程中对象的状态不会被修改,保证垃圾回收的准确性。
  • 多线程并行执行垃圾收集:一旦应用程序线程暂停,ParNew 收集器会启动多个线程并行执行垃圾回收操作。这些线程会同时在新生代中扫描和清理不再使用的对象,以释放内存空间。
  • 暂停用户线程:在整个垃圾收集过程中,用户线程会一直处于暂停状态,直到垃圾收集完成。这个阶段也被称为“Stop the World”,在此期间应用程序无法执行任何操作。
  • 恢复用户线程:当垃圾收集完成后,ParNew 收集器会恢复用户线程的执行。此时,垃圾已经被清理,堆中的内存空间得到了释放,用户线程可以继续执行。

ParNew 收集器的工作流程与 Parallel Scavenge 收集器类似,都是通过停止应用程序线程,然后利用多线程并行执行垃圾回收操作,最后恢复用户线程的执行。这种并行执行的方式能够提高垃圾收集的效率,同时在暂停用户线程期间确保垃圾收集的准确性。

注:
收集区域: 新生代
使用算法: 标记复制法
搜集方式: 多线程。

5. CMS收集器

随着硬件技术的发展,可用内存越来越大,这为应用程序提供了更多的内存空间,从而能够创建更多的对象,减少了垃圾收集的频率。然而,随着内存空间的增大,垃圾收集的时间也相应增加,可能导致长时间的停顿,影响用户体验。在这种情况下,传统的垃圾收集器需要暂停应用程序线程进行垃圾收集,这会导致用户在执行某些操作时出现延迟甚至停顿的情况,这是无法接受的。

CMS 垃圾收集器的设计初衷是允许垃圾收集器在进行垃圾回收的同时,与应用程序的线程并发执行,不需要长时间暂停应用程序线程。CMS 垃圾收集器通过并发标记和清除的方式,允许在垃圾收集过程中与应用程序并发执行,从而降低了垃圾收集的停顿时间,提高了系统的响应性和用户体验。

CMS 垃圾收集器的优势

  • 并发标记和清除:CMS 垃圾收集器采用了并发标记和清除的方式,允许在垃圾收集过程中与应用程序并发执行。这意味着垃圾收集过程中只有一小部分时间需要暂停应用程序线程。
  • 低停顿时间:由于并发执行的特性,CMS 垃圾收集器能够在较短的时间内完成垃圾回收操作,从而减少了应用程序的停顿时间。通常情况下,CMS 垃圾收集器能够将停顿时间控制在几百毫秒甚至更低。
  • 老年代收集:CMS 垃圾收集器主要针对老年代进行垃圾回收,对于新生代则通常使用 ParNew 收集器。这种分代收集的方式能够更好地适应不同内存区域的特点和垃圾回收需求。

垃圾收集流程
CMS(Concurrent Mark-Sweep)垃圾收集器为了尽量减少用户线程的停顿时间,采用了一种创新的策略。这一策略使得在垃圾回收过程的某些阶段,用户线程和垃圾回收线程可以共同工作,从而避免了长时间的垃圾回收导致用户线程一直处于等待状态。

整个 CMS 垃圾收集过程被划分为四个阶段,它们分别是:

  • 初始标记(Initial Mark):在这个阶段,CMS 垃圾收集器会对根对象进行一次快速的标记,标记出所有与根对象直接关联的存活对象。这个阶段需要暂停用户线程,因为要确保标记的准确性。
  • 并发标记(Concurrent Mark):在这个阶段,CMS 垃圾收集器会与用户线程并发执行,对整个堆进行标记。垃圾回收线程会在后台标记所有存活对象,而用户线程可以继续执行,不受影响。
  • 重新标记(Remark):在并发标记阶段结束后,CMS 垃圾收集器会进行一次重新标记,来处理在并发标记阶段发生变化的对象。这个阶段需要暂停用户线程,以确保标记的准确性。
  • 并发清理(Concurrent Sweep):在重新标记完成后,CMS 垃圾收集器会与用户线程并发执行,清理未标记的对象。垃圾回收线程会在后台清理不再使用的对象,而用户线程可以继续执行,不受影响。

通过将垃圾回收过程分为多个阶段,并在其中允许用户线程和垃圾回收线程并发执行,CMS 垃圾收集器成功地减少了用户线程的停顿时间。这种创新的并发垃圾收集策略提高了系统的响应性和用户体验,确保了应用程序的顺畅运行。

注:
收集区域: 老年代
使用算法: 标记清除法+标记整理法
搜集方式: 多线程

6. G1收集器

CMS 垃圾收集器开创了垃圾收集器的一个新时代,实现了垃圾收集和用户线程同时执行,从而达到了垃圾收集的过程不停止用户线程的目标。这种并发垃圾收集的思路为后续垃圾收集器的发展提供了重要的参考。

随着硬件资源的不断升级,可用的内存资源越来越多,这对于垃圾收集器的发展提出了新的挑战。传统的垃圾收集器采用物理分区的方式将内存分为老年代、新生代、永久代或 MetaSpace,但随着可用内存的增加,某一分代区域的大小可能会达到几十上百 GB。在这种情况下,传统的物理分区收集方式会导致垃圾扫描和清理时间变得更长,性能下降。

G1 垃圾收集器摒弃了传统的物理分区方式,而是将整个内存分成若干个大小不同的 Region 区域。每个 Region 在逻辑上组合成各个分代,这样做的好处是可以以 Region 为单位进行更细粒度的垃圾回收。G1 垃圾收集器在进行垃圾回收时,可以针对单个或多个 Region 进行回收,从而提高了收集效率和性能。

G1 垃圾收集器吸取了 CMS 垃圾收集器的优良思路,并通过摒弃物理分区、采用 Region 分区的方式,实现了更细粒度的垃圾回收,从而提高了整个系统的性能和可用性。 G1 垃圾收集器在大内存环境下的表现更加出色,成为了现代 Java 应用中的重要选择。

Region(局部收集)

G1 垃圾收集器的最核心分区基本单位是 Region。与传统的垃圾收集器不同,G1 不再将堆内存划分为固定连续的几块区域,而是完全舍弃了物理分区,而是将堆内存拆分成大小为 1MB 到 32MB 的 Region 块。然后,以 Region 为单位自由地组合成新生代、老年代、Eden 区、Survivor 区和大对象区(Humongous Region)等。随着垃圾回收和对象分配的进行,每个 Region 也不会一直固定属于某个分代,它们可以随时扮演任何一个分代区域的内存角色。

Collect Set(智能收集)

在G1里面会维护一个Collect Set集合。这个集合记录了待回收的 Region 块的信息,包括每个 Region 块可回收的大小空间。有了这个 CSet 信息,G1 在进行垃圾收集时可以根据用户设定的可接受停顿时间来进行分析,找出在设定的时间范围内收集哪些区域最划算,然后优先收集这些区域。这样做不仅可以优先收集垃圾最多的 Region,还可以根据用户的设定来计算收集哪些 Region 可以达到用户所期望的垃圾收集时间。

通过 CSet,G1 垃圾收集器的性能得到了极大的提升,并且能够实现可预测的停顿时间要求。这使得垃圾回收过程变得更加智能化,更加适应不同的应用场景和用户需求。需要注意的是,用户设定的时间应该合理,官方建议在 100ms 到 300ms 之间,以平衡垃圾收集的效率和停顿时间的需求。

垃圾收集流程
G1 垃圾收集器的回收流程与 CMS 的逻辑大致相同,包括初始标记、并发标记、重新标记和筛选清除等阶段。但是,与 CMS 不同的是,G1 在最后一个阶段不会直接进行整体的清除。相反,它会根据用户设置的停顿时间进行智能的筛选和局部的回收。

  • 初始标记(Initial Mark):在初始标记阶段,G1 垃圾收集器会对根对象进行一次快速的标记,标记出所有与根对象直接关联的存活对象。这个阶段需要暂停用户线程,以确保标记的准确性。
  • 并发标记(Concurrent Mark):在并发标记阶段,G1 垃圾收集器会与用户线程并发执行,对整个堆进行标记。垃圾回收线程会在后台标记所有存活对象,而用户线程可以继续执行,不受影响。
  • 重新标记(Remark):在并发标记阶段结束后,G1 垃圾收集器会进行一次重新标记,来处理在并发标记阶段发生变化的对象。这个阶段需要暂停用户线程,以确保标记的准确性。
  • 筛选清除(Concurrent Cleanup):在重新标记完成后,G1 垃圾收集器不会立即进行整体的清除操作。相反,它会根据用户设置的停顿时间智能地筛选出需要回收的 Region,并执行局部的回收。这样可以在尽量满足停顿时间的情况下,最大限度地回收垃圾。

通过这种智能的筛选和局部回收方式,G1 垃圾收集器能够更好地平衡垃圾回收的效率和停顿时间,从而提高系统的响应性和用户体验。

注:
收集区域: 整个堆内存
使用算法: 标记复制法
搜集方式: 多线程

7. ZGC收集器

ZGC(Z Garbage Collector)是一种低延迟的垃圾回收器,是 JDK 11 引入的一项重要特性。ZGC 的出现为 Java 应用提供了一种更加高效、可预测的垃圾回收解决方案,与传统的垃圾回收器相比,ZGC 的主要目标是实现极低的垃圾回收停顿时间,使得 Java 应用能够以更可预测的方式运行,尤其在大内存堆上表现良好。

ZGC 的优势和特点包括:

  • 低停顿时间:ZGC 致力于将垃圾回收的停顿时间降至最低。它通过并发标记、并发清理等技术,在整个垃圾回收过程中尽量减少对应用程序的影响,从而实现了极低的垃圾回收停顿时间。这使得 Java 应用能够更加平滑地运行,减少了因垃圾回收而导致的不可预测性和性能波动。

  • 可预测性:ZGC 的设计注重可预测性,即使在大内存堆上,也能够提供稳定的性能和可预测的垃圾回收行为。这使得开发人员能够更加信任和依赖于 Java 应用在生产环境中的稳定性和可靠性。

  • 适用于大内存堆:ZGC 的低停顿时间特性使其特别适用于大内存堆的场景。在这种场景下,传统的垃圾回收器可能会面临长时间的停顿,影响应用的响应性和用户体验,而 ZGC 能够有效地缓解这一问题,保持较低的停顿时间,从而确保应用的流畅运行。

垃圾收集流程
ZGC 的垃圾回收过程几乎全部都是并发执行的,即与应用程序线程同时进行。

  • 初始标记(Initial Mark):在初始标记阶段,ZGC 会标记出根对象以及直接与根对象关联的存活对象。这个阶段需要短暂地暂停所有应用线程,以确保标记的准确性。
  • 并发标记(Concurrent Mark):在并发标记阶段,ZGC 与应用程序线程并发执行,标记所有存活对象。这个阶段不会暂停应用程序线程,因此垃圾回收和应用程序可以并发执行。
  • 最终标记(Final Mark):在并发标记阶段结束后,ZGC 需要再次短暂地暂停所有应用线程,完成最终的标记工作。这个阶段主要用于标记在并发标记阶段有可能发生变化的对象。
  • 筛选(Concurrent Sweep):在最终标记完成后,ZGC 会进行一次筛选,确定哪些对象可以被回收。这个阶段会并发地进行,不会暂停应用程序线程。
  • 并发清除(Concurrent Cleanup):在筛选阶段完成后,ZGC 会并发地清除未被标记的对象,释放它们所占用的内存。这个阶段也不会暂停应用程序线程。

注:
收集区域: 整个堆内存
使用算法: 并发标记法
搜集方式: 多线程

  • 54
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVMJava虚拟机)垃圾回收是负责管理内存的重要组件。它负责清理不再使用的对象,以便为新对象腾出空间。在Java中,常见的垃圾回收有以下几种: 1. Serial(串行)垃圾回收:它是最早实现的垃圾回收,使用单线程进行垃圾回收。它会暂停应用程序的所有线程,并逐个扫描内存中的对象进行回收。虽然效率较低,但对于小型应用程序或单核处理的系统来是一个不错的选择。 2. Parallel(并行)垃圾回收:与串行垃圾回收类似,但它使用多个线程并行进行垃圾回收。这样可以提高回收效率,但需要更多的系统资源。它适用于多CPU系统,可以加快垃圾回收的速度。 3. CMS(Concurrent Mark and Sweep,并发标记清除)垃圾回收:它是一种并发回收,允许应用程序在垃圾回收过程中继续运行。它使用多个线程来标记和清除内存中的垃圾对象。此外,CMS使用了一种增量算法,避免了长时间的停顿时间,对于对停顿时间敏感的应用程序来是一种选择。 4. G1(Garbage First)垃圾回收:它是JDK 7中引入的一种新型垃圾回收。G1使用分区的方式管理堆内存,将整个堆分成多个小块。通过基于目标停顿时间的方式,它可以根据应用程序的需求来选择性地回收不再使用的对象,以减少停顿时间和提高吞吐量。 不同的垃圾回收适用于不同的应用场景,开发人员可以根据应用程序的需求和系统配置来选择合适的垃圾回收

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值