文章目录
- [一、JVM 常见垃圾回收算法](#一、JVM 常见垃圾回收算法)
-
- [1. 标记-清除(Mark-Sweep)](#1. 标记-清除(Mark-Sweep))
- [2. 标记-整理(Mark-Compact)](#2. 标记-整理(Mark-Compact))
- [3. 复制算法(Copying)](#3. 复制算法(Copying))
- [4. 分代收集算法(Generational Collection)](#4. 分代收集算法(Generational Collection))
- [5. 增量收集算法(Incremental Collection)](#5. 增量收集算法(Incremental Collection))
- [二、新型 GC 算法(G1、ZGC、Shenandoah 等)](#二、新型 GC 算法(G1、ZGC、Shenandoah 等))
-
- [1. G1 垃圾回收器(Garbage-First GC)](#1. G1 垃圾回收器(Garbage-First GC))
- [2. ZGC(Z Garbage Collector)](#2. ZGC(Z Garbage Collector))
- [3. Shenandoah 垃圾回收器](#3. Shenandoah 垃圾回收器)
- [三、三色标记法:现代 GC 算法的基础](#三、三色标记法:现代 GC 算法的基础)
- [四、总结:GC 算法的演进与优化](#四、总结:GC 算法的演进与优化)
垃圾回收(Garbage Collection,GC)是 Java 程序中非常重要的部分,涉及到内存的自动管理。不同的垃圾回收器(GC)使用不同的算法来完成内存回收任务。随着 Java 版本的更新,垃圾回收算法也在不断演进,特别是 JDK 1.8 之后 ,引入了新的 GC 算法,比如 三色标记法 和 ZGC 等。
一、JVM 常见垃圾回收算法
JVM 中的垃圾回收器并非只有一种,而是有多种不同的算法来实现垃圾回收。每种算法有其适用的场景和优缺点。我们将从 经典的垃圾回收算法 入手,逐步解析现代的 新型算法。
1. 标记-清除(Mark-Sweep)
流程:
- 标记阶段:从 GC Roots 开始标记所有存活的对象。
- 清除阶段:回收所有未标记的对象(即垃圾对象)。
优点:
- 简单直观。
- 不需要整理对象内存,适用于大多数简单的垃圾回收需求。
缺点:
- 内存碎片:因为对象没有按顺序清理,可能会产生内存碎片。
- 不高效:全停顿的标记过程可能会导致长时间的停顿。
2. 标记-整理(Mark-Compact)
在标记-清除的基础上,标记之后会整理存活对象的位置。
流程:
- 标记阶段:与标记-清除相同,标记存活对象。
- 整理阶段:将所有存活对象移动到堆的一端,整理后释放掉无用的空间。
优点:
- 解决内存碎片:整理步骤减少了碎片化,能有效利用内存。
- 对老年代尤为适用。
缺点:
- 对象的移动会导致额外的开销,需要停止应用线程。
- 整理过程可能比标记过程还要复杂。
3. 复制算法(Copying)
复制算法将内存分为两块区域,使用一块区域存放活动对象,另一块作为备用。垃圾回收时,将存活的对象从一块区域复制到另一块。
流程:
- 将堆划分为两个区域:Eden区 和 Survivor区。
- 新生代(年轻代)使用复制算法,每次进行垃圾回收时,存活的对象被复制到另一块区域。
- 剩余的空间被回收。
优点:
- 没有碎片:使用一块区域存储存活对象,直接复制到另一块区域。
- 适合频繁创建和销毁对象的场景(例如年轻代)。
缺点:
- 需要两倍的内存空间来进行复制。
- 只适用于年轻代(新生代)。
4. 分代收集算法(Generational Collection)
JVM 中的垃圾回收通常会使用 分代收集算法 ,将内存划分为不同的区域进行回收。分代收集算法的核心思想是:大部分对象都是短命的,只有少部分对象是长期存在的。
流程:
- 年轻代(Young Generation):对象大多在年轻代中生成和销毁,使用复制算法。
- 老年代(Old Generation):长期存活的对象会被移到老年代,使用标记-整理算法。
优点:
- 提升回收效率:年轻代采用复制算法,老年代采用标记-整理或标记-清除算法。
- 分代算法使得垃圾回收针对不同生命周期的对象有不同的优化策略。
缺点:
- 对老年代的回收仍然有较高的停顿时间。
- GC 可能会涉及到多个区域的回收。
5. 增量收集算法(Incremental Collection)
增量收集是对标记-清除算法的一种改进,它将标记过程分割成多个小阶段,减少每次标记的停顿时间。
优点:
- 减少停顿时间:通过增量方式逐步进行标记,降低全停顿的时间。
缺点:
- 仍然可能存在标记失败的情况,处理起来较为复杂。
二、新型 GC 算法(G1、ZGC、Shenandoah 等)
1. G1 垃圾回收器(Garbage-First GC)
G1 是为了替代 CMS(并发标记清除)而设计的一种新的垃圾回收器。它将堆划分为多个大小相等的 Region ,每个 Region 可以动态调整为 年轻代 、老年代 或 Humongous 区域(用于存放大对象)。
特点:
- 低停顿目标:G1 通过分区和增量标记减少了 STW 停顿时间,能够控制每次停顿的最大时长。
- 并行和并发:G1 支持多线程并行回收,且垃圾回收的各个阶段可以并发执行。
优点:
- 适用于需要低延迟的应用(如 Web 应用、实时系统)。
缺点:
- 对大堆内存的支持不如 CMS,可能在内存很大的系统中表现不如预期。
2. ZGC(Z Garbage Collector)
ZGC 是一个低延迟垃圾回收器,旨在通过 并发标记 和 并发整理 实现最小的停顿时间。它可以在 数百GB 的堆内存中运行,停顿时间几乎可以忽略不计。
特点:
- 几乎零停顿:ZGC 的停顿时间通常在 10 毫秒以内。
- 并发回收:几乎所有的垃圾回收都在并发线程中完成。
优点:
- 适用于大内存和低延迟的系统。
缺点:
- 内存开销较大,需要更多的资源来管理堆。
- 目前仅在 JDK 8+ 版本提供支持。
3. Shenandoah 垃圾回收器
Shenandoah 是与 ZGC 类似的一个低延迟垃圾回收器,它同样支持 并发标记 和 并发整理。Shenandoah 主要用在需要极低停顿的环境中。
特点:
- 低停顿:像 ZGC 一样,Shenandoah 通过减少 STW 停顿来优化性能。
- 内存碎片管理:通过并发整理,Shenandoah 可以减少内存碎片。
优点:
- 在大内存系统和实时应用中表现出色。
- 适合对低停顿要求高的应用场景。
缺点:
- 支持的 JDK 版本较少。
- 仍在优化阶段,性能上有时不如 G1。
三、三色标记法:现代 GC 算法的基础
三色标记法是现代垃圾回收算法(如 CMS、G1、ZGC、Shenandoah)背后的核心技术。它的基本思想是:
- 白色:表示不可达的对象,会被回收。
- 灰色:表示已标记但子对象未扫描。
- 黑色:表示已标记且所有子对象扫描完毕。
为什么使用三色标记法?
- 解决了并发标记中的"漏标"问题。
- 通过写屏障(Write Barrier)解决了"黑色对象引用白色对象"的问题。
四、总结:GC 算法的演进与优化
在 Java 的垃圾回收算法中,JDK 1.8 之前的常见算法主要依赖 标记-清除、标记-整理 和 复制算法 。这些算法虽然简单,但面临 内存碎片 、回收效率 和 停顿时间 等问题。
现代的 G1、ZGC、Shenandoah 等算法则采用了 三色标记法 、并发标记 和 低停顿优化 ,使得大内存、高并发应用能够更高效地进行垃圾回收,特别是 低延迟 和 实时性要求 高的应用。
对于新技术的理解:
- ZGC 和 Shenandoah :代表了 低延迟、高并发 的未来方向,适合大内存、低停顿的场景。
- G1 和 CMS :仍然广泛应用于传统系统,尤其是对于 可控的停顿时间 和 堆内存管理 的场景。