看了《深入理解 Java 虚拟机》一书,发现"垃圾收集器"在实际项目中还是有它的应用场景,比如你可 能需要为你的项目选择合适的垃圾收集器(一般情况下不会),那么就需要对各种垃圾收集器有一个整体的了解。
在讲解具体收集器前,需先明确几个关键维度(这是区分收集器的核心):

Java 虚拟机的垃圾回收器可以分为四大类别:串行收集器、并行收集器、CMS 收集器、G1 收集器在 HotSpot 虚拟机里面实现了七种作用于不同分代的收集器。

如果两个收集器之间存在连线,就说明它们可以搭配使用 ,图中收集器所处的区域,则表示它是属于 新生代收集器抑或是老年代收集器。

Serial 收集器
**Serial 收集器是最基础、历史最悠久的收集器,是一个单线程工作的收集器。**使用 Serial 收集器,无论是进行 Minor gc 还是 Full GC ,清理堆空间时,**所有的应用线程都会被暂停。**进行Full GC 时,它还会对老年代空间的对象进行压缩整理。通过 -XX:+UseSerialgGC 标志可以启用 Serial 收集器。

对于单核处理器或处理器核心数较少的环境来说,Serial 收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得**最高的单线程收集效率。**Serial 收集器对于运行在客户端模式下的虚拟机来说是一个很好的选择。
- 原理:单线程执行新生代回收,全程 STW(暂停所有用户线程)。工作流程:Eden 区满时触发 GC → 复制 Eden+S0 存活对象到 S1 → 清空 Eden+S0 → 交换 S0/S1 角色。
- 特点:✅ 优点:实现简单、无线程切换开销,内存占用小;❌ 缺点:单线程效率低,STW 时间长(堆内存越大,停顿越久)。
- 适用场景:客户端程序(如桌面应用)、小内存场景(堆 < 1G)、单核 CPU 环境。
- 参数配置 :
-XX:+UseSerialGC(启用 Serial+Serial Old 组合)。
ParNew 收集器
**ParNew 收集器实质上是 Serial 收集器的多线程并行版本。**除了同时使用多条线程进行垃圾收集之外,其余的行为包括 Serial 收集器可用的所有控制参数、收集算法、Stop The World、 对象分配规则、回收策略等都与 Serial 收集器完全一致。

ParNew 收集器在单核心处理器的环境中绝对不会有比 Serial 收集器更好的效果,甚至由于存在线程交互的开销,该收集器在通过超线程(Hyper-Threading)技术实现的伪双核处理器环境中都不能百分之百保证超越 Serial 收集器。是 JDK 7 之前的遗留系统中首选的新生代收集器。
- 原理:Serial 的多线程版本,核心逻辑与 Serial 一致,只是用多线程执行复制操作。工作流程:与 Serial 相同,仅回收线程数可配置(默认等于 CPU 核心数)。
- 特点:✅ 优点:多线程提升新生代回收效率,STW 时间短于 Serial;❌ 缺点:线程切换有开销,单核环境下性能不如 Serial。
- 适用场景:服务端程序(如 WEB 应用),常与 CMS 老年代收集器搭配(CMS 默认新生代用 ParNew)。
- 参数配置 :
-XX:+UseParNewGC(启用 ParNew)、-XX:ParallelGCThreads(设置回收线程数,如-XX:ParallelGCThreads=4)。
Parallel Scavenge 收集器
Parallel Scavenge 收集器也是一款新生代收集器,基于标记------复制算法实现,能够并行 收集的多线程收集器和 ParNew 非常相似。
Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量 就是处理器用于运行用户代码的时间与处理器总消耗时间的比值。如果虚拟机完成某个任务,用 户代码加上垃圾收集总共耗费了 100 分钟,其中垃圾收集花掉 1 分钟,那吞吐量就是 99%。

- 原理:ParNew 的「吞吐量优化版本」,同样是多线程复制回收,但更关注「吞吐量」(吞吐量 = 用户代码执行时间 /(用户代码执行时间 + GC 时间))。支持「自适应调节策略」(-XX:+UseAdaptiveSizePolicy):JVM 自动调整新生代大小、Survivor 比例、晋升年龄,无需手动调参。
- 特点:✅ 优点:吞吐量优先,适合计算密集型任务;自适应调节简化调优;❌ 缺点:仍为独占式回收(全程 STW),对延迟敏感场景不友好。
- 适用场景:大数据批处理、后台任务(如报表生成)、吞吐量优先的场景。
- 参数配置 :
-XX:+UseParallelGC(启用 Parallel Scavenge+Parallel Old 组合)、-XX:MaxGCPauseMillis(目标最大 STW 时间,JVM 会尝试达标)、-XX:GCTimeRatio(吞吐量比例,如-XX:GCTimeRatio=99表示 GC 时间占比≤1%)。
Serial Old 收集器
Serial Old 是 Serial 收集器的老年代版本,它同样是一个单线程收集器,使用 "标记-整理 算法"。

- 原理:Serial 的老年代版本,单线程执行「标记 - 整理」算法(标记存活对象→将存活对象向内存一端移动→清除末端无用内存)。全程 STW。
- 特点:与 Serial 一致,简单但效率低。
- 适用场景:客户端程序、小内存场景,或作为 CMS 收集器的「后备方案」(CMS 并发失败时,Serial Old 会接管老年代回收)。
- 参数配置 :配合
-XX:+UseSerialGC启用。
Parallel Old 收集器
Parallel Old 是 Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,基于标记 -整理算法实现。


使用 java -XX:+PrintCommandLineFlags -version 命令可以查看
- 原理:Parallel Scavenge 的老年代版本,多线程执行「标记 - 整理」算法,全程 STW。与 Parallel Scavenge 搭配,形成「新生代并行 + 老年代并行」的全并行回收组合。
- 特点:吞吐量高,STW 时间短于 Serial Old,但仍为独占式回收。
- 适用场景:与 Parallel Scavenge 配套,用于吞吐量优先的场景(如批处理)。
- 参数配置 :配合
-XX:+UseParallelGC启用,或单独用-XX:+UseParallelOldGC。
CMS 收集器
**CMS 收集器设计的初衷是为了消除 Parallel 收集器和 Serial 收集器 Full gc 周期中的长时间停顿。**CMS 收集器在 Minor gc 时会暂停所有的应用线程,并以多线程的方式进行垃圾回收。CMS 收集器基于标记-清除算法实现的,整个过程分为四个步骤, 整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。
垃圾回收过程如下:
- 初始标记(CMS initial mark ):初始标记仅仅只是标记一下GC Roots 能直接关联到的对象,速度很快; 仍然需要"Stop The World"。
- 并发标记(CMS concurrent mark ):并发标记阶段就是从GC Roots 的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
- 重新标记(CMS remark):重新标记阶段则是为了修正并发标记期间,因为用户程序继续运作而导致 标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但 也远比并发标记阶段的时间短。
- 并发清除(CMS concurrent sweep):并发清除阶段,清理删除掉标记阶段判断的已经死亡的对象, 由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。

CMS 收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的 Java 应用集中在互联网网站或者基于浏览器的 B/S 系统的服务端上,这类应用通常都会较为关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来良好的交互体验。CMS 收集器就非常符合这类应用的需求。
- 原理 :基于「标记 - 清除」算法,分为 4 个阶段:
- 初始标记:STW,标记 GC Roots 直接关联的老年代对象(速度快,停顿短);
- 并发标记:与用户线程并行,遍历标记存活对象(无 STW,但可能因用户线程修改对象导致标记不准确);
- 重新标记:STW,修正并发标记期间被修改的对象标记(停顿比初始标记长,但远短于独占式回收);
- 并发清除:与用户线程并行,清除未标记的无用对象(无 STW)。
- 特点 :✅ 优点:并发回收,STW 时间极短(毫秒级),适合低延迟场景;❌ 缺点:
- 内存碎片:标记 - 清除算法不整理内存,长期运行会产生碎片,导致大对象无法分配而触发 Full GC;
- CPU 敏感:并发阶段占用 CPU 资源,CPU 核心少的场景下会降低用户程序吞吐量;
- 并发失败:老年代内存增长过快时,CMS 来不及回收,会触发「Concurrent Mode Failure」,转而用 Serial Old 回收(导致长时间 STW)。
- 适用场景:WEB 服务、电商秒杀、金融交易等低延迟场景(JDK9 已废弃,被 G1 替代)。
- 参数配置 :
-XX:+UseConcMarkSweepGC(启用 CMS+ParNew 组合)、-XX:CMSInitiatingOccupancyFraction(老年代占用比例阈值,触发 CMS 回收,如-XX:CMSInitiatingOccupancyFraction=75)、-XX:+UseCMSCompactAtFullCollection(Full GC 时整理内存,减少碎片)。
Garbage First 收集器(G1)
G1 回收器是 JDK 1.7 中使用的全新垃圾回收器,从长期目标来看,其是为了取代 CMS 回收器。
G1 回收器拥有独特的垃圾回收策略,和之前所有垃圾回收器采用的垃圾回收策略不同。从分代看,G1 依然属于分代垃圾回收器。但它最大的改变是使用了分区算法,从而使得 Eden 区、 From 区、Survivor 区和老年代等各块内存不必连续。
在 G1 回收器之前,所有的垃圾回收器其内存分配都是连续的一块内存,如下图所示。 
而在 G1 回收器中,其将一大块的内存分为许多细小的区块,从而不要求内存是连续的。

从上图可以看到,每个 Region 被标记了 E、S、O 和 H,说明每个 Region 在运行时都充当了一种角色。所有标记为 E 的都是 Eden 区的内存,它们散落在内存的各个角落,并不要求内存连续。同理,Survivor 区、老年代(Old)也是如此。
从上图我们还可以看到 H 是以往算法中没有的,它代表 Humongous。这表示这些 Region 存储的是巨型对象(humongous object,H-obj),当新建对象大小超过 Region 大小一半时,直接在新的一个或多个连续 Region 中分配,并标记为 H。
堆内存中一个 Region 的大小可以通过 -XX:G1HeapRegionSize 参数指定,大小区间只能是 1M、2M、4M、8M、16M 和 32M,总之是 2 的幂次方。如果 G1HeapRegionSize为 默认值,即把设置的最小堆内存按照 2048 份均分,最后得到一个合理的大小。
G1 收集器的收集过程主要有四个阶段:
- 新生代 GC
- 并发标记周期
- 混合收集
- 如果需要,可能进行 FullGC
新生代 GC 与其他垃圾收集器的类似,就是清空 Eden 区,将存活对象移动到 Survivor 区,部分年龄到了就移动到老年代。
并发标记周期则分为:初始标记、根区域扫描、并发标记、重新标记、独占清理、并发清理阶段。其中初始标记、重新标记、独占清理是独占式的,会引起停顿。并且初始标记会引发一次新生代 GC。在这个阶段,所有将要被回收的区域会被 G1 记录在一个称之为 Collection Set 的 集合中。
混合回收阶段会首先针对 Collection Set 中的内存进行回收,因为这些垃圾比例较高。G1 回收器的名字 Garbage First 就是这个意思,垃圾优先处理的意思。在混合回收的时候,也会执行多次新生代 GC 和 混合 GC,从而来进行内存的回收。
必要时进行 Full GC。当在回收阶段遇到内存不足时,G1 会停止垃圾回收并进行一次 Full GC,从而腾出更多空间进行垃圾回收。

相关参数
打开 G1 收集器,我们可以使用参数:-XX:+UseG1GC。
设置目标最大停顿时间,可以使用参数:-XX:MaxGCPauseMillis。
设置 GC 工作线程数量,可以使用参数:-XX:ParallelGCThreads。
设置堆使用率触发并发标记周期的执行,可以使用参数:-XX:InitiatingHeapOccupancyPercent。
从一开始的串行回收器,到后来的并行回收器、CMS 回收器,到最后的 G1 回收器,垃圾回收器不断改进,使得垃圾回收效率不断提升。特别是分区思想诞生后,对于垃圾回收停顿时间的控制更加细腻,可以让应用有更完美的延时控制,从而呈现更好的用户体验。
- 原理 :
- 分区管理:将堆内存划分为多个大小相等的 Region(1M~32M),每个 Region 可动态充当 Eden/Survivor/Old 区(打破固定分代);
- Remembered Set:每个 Region 维护 Remembered Set(记录跨 Region 引用),避免全堆扫描;
- 回收流程 :
- Young GC:回收 Eden+Survivor Region,复制存活对象到新 Region(STW,多线程);
- 混合回收(Mixed GC):老年代 Region 占用达阈值时触发,回收 Eden+Survivor + 部分老年代 Region(部分阶段并发);
- Full GC:G1 回退方案(如内存不足时),采用标记 - 整理算法(STW,应尽量避免)。
- 特点 :✅ 优点:
- 兼顾吞吐量与延迟,可设置最大 STW 时间(
-XX:MaxGCPauseMillis); - 分区回收减少碎片,无需单独整理内存;
- 适配大内存(4G~16G)场景;❌ 缺点:
- Remembered Set 占用额外内存(约堆内存的 5%~10%);
- 小内存场景下性能不如 Parallel GC。
- 兼顾吞吐量与延迟,可设置最大 STW 时间(
- 适用场景:中等堆内存(4G~16G)、混合业务场景(兼顾吞吐量与延迟)。
- 参数配置 :
-XX:+UseG1GC(启用 G1)、-XX:MaxGCPauseMillis(目标最大 STW 时间,如-XX:MaxGCPauseMillis=200)、-XX:G1HeapRegionSize(Region 大小,默认根据堆内存自动分配)、-XX:InitiatingHeapOccupancyPercent(堆占用阈值,触发混合回收,默认 45%)。
ZGC
ZGC(Z Garbage Collector) 是一款性能比G1 更加优秀的垃圾收集器。ZGC 第一次出 现是在JDK 11 中以实验性的特性引入,这也是JDK 11 中最大的亮点。在JDK 15 中ZGC 不 再是实验功能,可以正式投入生产使用了。
目标低延迟
- 保证最大停顿时间在几毫秒之内,不管你堆多大或者存活的对象有多少。
- 可以处理 8MB-16TB 的堆
通过以上历代垃圾回收器的讲解,我们大致了解到减少延迟的底层思想不外乎将 stop the world 进行极限压缩,将能并行的部分全部采用和用户线程并行的方式处理,然而 ZGC 更"过分"它甚至把一分部垃圾回收的工作交给了用户线程去做,那么它是怎么做到的呢?ZGC 的标记和清理工作同CMS、G1 大致差不多,仔细看下图的过程,和 CMS 特别像,CMS 其实并没有真正被抛弃,它的部分思想在 ZGC 有发扬。

ZGC 的步骤大致可分为三大阶段分别是标记、转移、重定位。
标记:从根开始标记所有存活对象
转移:选择部分活跃对象转移到新的内存空间上
重定位:因为对象地址变了,所以之前指向老对象的指针都要换到新对象地址上。
并且这三个阶段都是并发的。
初始转移需要扫描 GC Roots 直接引用的对象并进行转移,这个过程需要 STW, STW 时间跟 GC Roots 成正比。
并发转移准备 :分析最有回收价值 GC 分页(无 STW) 初始转移应对初始标记的数据
并发转移应对并发标记的数据
标记清理过程继承了 CMS 和 G1 的思想
ZGC 优劣
ZGC 在战略上沿用了上几代 GC 的算法策略,采用并发标记,并发清理的思路,在战术上, 通过染色指针、多重映射,读屏障等优化达到更理想的并发清理,通过支持 NUMA 达到了更快的内存操作。但 ZGC 同样不是银弹,它也有自身的优缺点,如下:
优势:
- 一旦某个 Region 的存活对象被移走之后,这个 Region 立即就能够被释放和重用掉,而不必等待整个堆中所有指向该 Region 的引用都被修正后才能清理,这使得理论上只要还有一个空闲 Region,ZGC 就能完成收集。
- 颜色指针可以大幅减少在垃圾收集过程中内存屏障的使用数量,ZGC 只使用了读屏障。
- 颜色指针具备强大的扩展性,它可以作为一种可扩展的存储结构用来记录更多与对象标记、 重定位过程相关的数据,以便日后进一步提高性能。
劣势:
- 它能承受的对象分配速率不会太高
ZGC 准备要对一个很大的堆做一次完整的并发收集。在这段时间里面,由于应用的对象分配速率很高,将创造大量的新对象,这些新对象很难进入当次收集的标记范围,通常就只能全部当作存活对象来看待------尽管其中绝大部分对象都是朝生夕灭的,这就产生了大量的浮动垃圾。如果这种高速分配持续维持的话,每一次完整的并发收集周期都会很长,回收到的内存空间持续小于期间并发产生的浮动垃圾所占的空间,堆中剩余可腾挪的空间就越来越小了。目前唯一的办法就是尽可能地增加堆容量大小,获得更多喘息的时间。
- 吞吐量低于 G1 GC
一般来说,可能会下降 5%-15%。对于堆越小,这个效应越明显,堆非常大的时候,比如100G,其他 GC 可能一次 Major 或 Full GC 要几十秒以上,但是对于 ZGC 不需要那么大暂停。这种细粒度的优化带来的副作用就是,把很多环节其他 GC 里的 STW 整体处理,拆碎了,放到了更大时间范围内里去跟业务线程并发执行,甚至会直接让业务线程帮忙做一些 GC 的操作,从而降低了业务线程的处理能力。
- 核心技术 :
- 着色指针:将对象标记信息存储在指针的额外位(而非对象头),避免修改对象头的开销;
- 读屏障:访问对象时触发读屏障,动态跟踪对象引用变化,无需全程 STW;
- 分区回收:与 G1 类似,但 Region 可动态扩容 / 收缩,支持超大堆。
- 回收流程 :
- 并发标记:遍历标记存活对象(无 STW);
- 并发重分配:将存活对象复制到新 Region(部分阶段 STW,毫秒级);
- 并发重映射:更新指针指向新 Region(无 STW)。
- 特点 :✅ 优点:
- STW 时间极短(<10ms),几乎可忽略;
- 支持超大堆(TB 级),适合内存密集型应用;
- 无内存碎片(复制算法);❌ 缺点:CPU 占用高(读屏障、着色指针带来开销)。
- 适用场景:超大堆(>16G)、超低延迟场景(如实时风控、高频交易)。
- 参数配置 :
-XX:+UseZGC(启用 ZGC)、-XX:ZHeapSize(堆大小,如-XX:ZHeapSize=32G)。
总结
其实 ZGC 并不是一个凭空冒出的全新垃圾回收,它结合前几代 GC 的思想,同时在战术上做了优化以达到极限的 STW,ZGC 的优秀表现有可能会改变未来程序编写方式,站在垃圾收集器的角度,垃圾收集器特别喜欢不可变对象,原有编程方式鉴于内存、GC 能力所限使用可变对象来复用对象而不是销毁重建,试想如果有了 ZGC 的强大回收能力的加持,是不是我们就可以无脑的使用不可变对象进行代码编写。
垃圾收集器对比
HotSpot VM 中的垃圾回收器,以及适用场景:

下面是另外一个网友给出的各垃圾收集器对比:

Serial:新生代 单线程
ParNew :新生代 多线程
Pararrel Scavenge: 新生代 多线程 吞吐量
Serial Old:老年代 单线程
Pararrel Old:老年代 多线程 吞吐量
CMS : 老年代 多线程 标记---清除算法,可以与用户线程同时运行
G1:可以回收新生代、老年代,多线程,采用 Region 管理,评估垃圾最多的 Region,进行垃圾回收,用尽可能短的时间,获得最高的回收效率