JVM垃圾回收机制深度解析(G1篇)(垃圾回收过程及专业名词详解)

前言:今日通过问答的形式讲解一下JVM内存回收的相关知识点

Q1:Java中的young gc,old gc,full gc和mixed gc的区别是什么?

A1:

GC 类型 回收区域 STW 停顿 典型触发条件 主流收集器支持 关键特征
Young GC (Minor GC) 仅新生代(Eden + Survivor) ✅(短) Eden 区满 所有分代收集器(Serial/Parallel/CMS/G1) 高频、停顿短(ms级),复制算法
Old GC 仅老年代 ⚠️依收集器 老年代空间不足(特定场景) 极少独立存在(见下文详解) 术语模糊:CMS 无独立 Old GC 事件;Serial Old/Parallel Old 通常作为 Full GC 的一部分
Mixed GC 新生代 + 部分老年代 Region ✅(可控) G1 完成并发标记后,老年代占用 ≥ IHOP(默认45%) G1 专属 CSet 动态选择"回收价值高"的老年代 Region,平衡停顿与回收效率
Full GC 全堆(新生代+老年代)+ 方法区(元空间) ✅(长) 多种(见下文) 所有收集器(但 ZGC/Shenandoah 停顿极短) 应用暂停时间长(百 ms~秒级),需重点监控与避免

深度解析与关键澄清

🔹 Young GC(Minor GC)

  • 过程:清空 Eden + From Survivor,存活对象复制到 To Survivor(或晋升老年代)。
  • 触发时机 :Eden区域满了就开始进行GC
  • 频率:最高(秒级/分钟级),属正常现象。

🔹 Old GC

触发时机:老年代空间不足时触发

🔹 Mixed GC(G1 专属)

  • 触发条件
    InitiatingHeapOccupancyPercent(IHOP,默认45%)→ 完成并发标记 → 后续 Young GC 转为 Mixed GC。
  • CSet 构成
    所有年轻代 Region + 按"回收价值"排序的老年代 Region(垃圾比例高、回收收益大)。
  • 目的 :在用户设定的最大停顿时间(-XX:MaxGCPauseMillis)内,渐进清理老年代,避免 Full GC。
  • 日志特征
    [GC pause (G1 Evacuation Pause) (mixed), 0.025s]

🔹 Full GC ------ 高危事件!

  • 核心触发条件 (需重点规避):

    触发场景 说明
    老年代空间不足 晋升失败、大对象分配失败
    元空间(Metaspace)耗尽 -XX:MaxMetaspaceSize 设置过小
    显式调用 System.gc() 且未设置 -XX:+DisableExplicitGC
    担保失败 Young GC 前,老年代剩余空间 < Eden+Survivor 所有对象大小
    CMS 并发失败 Concurrent Mode Failure → 退化为 Serial Old Full GC
    G1 混合回收失败 to-space exhausted → Evacuation Failure
  • full gc当老年代空间不足或者元空间不足;还有调用system.gc建议敏发ful gc,但是不一定执行。会回收老年代和新生代。

  • 影响:长时间 STW,服务卡顿,TPS 骤降。


收集器视角对比(关键差异)

收集器 Young GC Mixed GC Full GC 触发特点 备注
Serial / Parallel ❌ 无 老年代回收 = Full GC(Serial Old/Parallel Old) Parallel Scavenge + Parallel Old 组合中,老年代回收即 Full GC
CMS 有(ParNew) ❌ 无 Concurrent Mode Failure 时退化为 Serial Old Full GC CMS 本身无 Full GC,但失败后代价极高
G1 ✅ 有 Mixed GC 失败(Evacuation Failure)或元空间不足 Mixed GC 是 G1 避免 Full GC 的核心机制
ZGC / Shenandoah 无(逻辑分代可选) ❌ 无 有(但停顿 < 10ms),日志称 "Pause Mark Start" 等 不分代设计,传统术语不适用;追求"无感 GC"

💡 重要提示

  • "Major GC" 是模糊术语 :部分资料指 Old GC,部分指 Full GC。强烈建议避免使用,改用具体事件名(如 "CMS Remark"、"G1 Mixed GC")。
  • ZGC/Shenandoah:虽有"全堆回收"动作,但因并发标记/转移,停顿极短,日志中不称 "Full GC",需结合具体收集器文档理解。

Q2:Java中常见的垃圾回收器有哪些?

A2:

收集器 定位 核心优势 适用人群
Serial 简单 无开销 嵌入式/学习
Parallel 吞吐 高吞吐 后台计算
CMS 低延迟 停顿短 已废弃
G1 平衡 可预测停顿 大多数服务端应用
ZGC 极致低延迟 <10ms 金融/实时系统
Shenandoah 极致低延迟 <10ms ZGC 替代方案
Epsilon 无 GC 零开销 测试/短生命周期

Q3:G1回收器的垃圾回收流程是怎么样子的?

A3:

下图来自https://www.mianshiya.com/

h存放超过region区域一半的大对象

G1(Garbage-First,垃圾优先)垃圾回收器的回收流程主要分为以下几个阶段:

1. 初始标记(Initial Mark)

  • 暂停所有应用线程(STW)。
  • 标记 GC Roots 能直接关联到的对象,速度快。
  • 修改 TAMS(Next Top at Mark Start)的值。

2. 并发标记(Concurrent Marking)

  • 与应用程序线程并发执行。
  • 从 GC Roots 遍历对象图,标记存活对象,处理并发操作对标记的影响。

3. 最终标记(Final Mark)

  • 暂停应用线程(STW)。
  • 处理并发标记阶段的 SATB(Snapshot At The Beginning)记录,保证标记完整性。

4. 筛选回收(Live Data Counting and Evacuation)

  • 暂停应用线程(STW)。
  • 统计 Region 中存活和可回收对象信息。
  • 按回收收益排序 Region,优先回收收益高的,复制存活对象到其他 Region,释放被回收 Region 空间。

G1 垃圾回收器通过分阶段回收,减少对应用程序的影响,提升内存回收效率,尤其适合大堆内存管理。

以下是对上面出现的名词的解释:

1.GC Roots 是什么?

在 Java 垃圾回收机制中,GC Roots 是一组必须活跃的引用,是垃圾回收器在标记存活对象时的起始点集合。也就是说,从这些 GC Roots 开始遍历,能直接或间接访问到的对象都被认为是存活的对象,不会被垃圾回收器回收;而那些无法从 GC Roots 访问到的对象,则被判定为可回收的垃圾对象。

常见的 GC Roots 包括以下几类:

  • 虚拟机栈(栈帧中的本地变量表)中的引用:每个 Java 线程都有一个虚拟机栈,栈帧中包含局部变量表。局部变量表中引用的对象,比如方法中的参数、局部变量等所指向的对象,都是存活对象,因为只要线程还在执行相关方法,这些对象就可能会被使用,所以它们属于 GC Roots。
  • 方法区中类静态属性引用的对象 :在 Java 的方法区中存储着类的静态变量等信息。如果一个类的静态属性引用了某个对象,那么这个对象也是存活的。比如一个类有一个静态的 List,这个 List 中引用的对象都不会被回收。
  • 方法区中常量引用的对象 :方法区中的常量,如 final 修饰的基本数据类型常量和对象引用常量。被这些常量引用的对象同样被视为存活对象。
  • 本地方法栈中 JNI(Java Native Interface)引用的对象:当 Java 代码通过 JNI 调用本地(Native)方法时,本地方法栈中会保存一些引用,这些引用指向的对象也是存活的,会被当作 GC Roots。

【注:JNI(Java Native Interface)是Java 本地接口,是一种允许 Java 代码与其他编程语言(如 C、C++)编写的代码进行交互的技术,因为如文件系统操作等底层是 C、C++ 等语言编写的。】

2.TAMS(Next Top at Mark Start,标记开始时的下一个顶部位置)是什么?

  • TAMS 是一个地址标记:在每个 Region(G1 垃圾回收器将堆内存划分为多个大小相等的 Region(块))中,都有一个 TAMS 指针。当并发标记开始时,会记录下此时 Region 中的一个地址,这个地址就是 TAMS。
  • 划分对象创建时间:TAMS 将 Region 中的对象划分为两部分。在并发标记开始之前创建的对象位于 TAMS 指针之前;而在并发标记过程中创建的新对象会被分配到 TAMS 指针之后的空间。
  • 而所谓的 "修改 TAMS 的值",其实是在初始标记阶段,会将 TAMS 指针设置到当前 Region 中对象分配的最新位置。

【注:"TAMS 指针前" 指的是在每个 Region 内,相对于 TAMS 指针位置而言,地址小于 TAMS 指针所指向地址的那部分内存空间。】

【注:对于 TAMS 之后的新对象,会隐式地认为是存活对象,不需要在并发标记阶段进行额外标记,简化了并发标记流程。】

【注:并发标记:在 G1 垃圾回收器的并发标记阶段,存在着垃圾回收线程与应用程序线程同时运行的状态,这种同时运行的状态即被称为并发过程。】

3.什么是对象图?

在 Java 程序运行时,众多对象被创建并存储于堆内存中,这些对象之间会通过引用彼此关联。对象图就是把这些对象当作节点,对象间的引用当作边,从而构成的一个有向图。

垃圾回收器借助从 GC Roots 开始遍历对象图,能够找出所有存活的对象。从 GC Roots 出发,沿着对象图中的引用关系不断访问其他对象,所有能够被访问到的对象都被判定为存活对象;而那些无法从 GC Roots 访问到的对象则被视为垃圾对象,可被垃圾回收器回收。

4.什么是"处理并发操作对标记的影响"?

在垃圾回收的并发标记阶段,"处理并发操作对标记的影响" 是指应对应用程序与垃圾回收线程同时运行致对象状态变化(创建、销毁、引用变更等),所采取的保标记准确措施:

  • 对象创建:如 G1 中 TAMS 机制,将并发标记时新对象置于 TAMS 指针后,隐式视其存活,免复杂标记。
  • 对象销毁:用 SATB 技术,初始快照结合写屏障记录引用删除,最终标记时调整结果,确可回收对象被识别。
  • 引用关系变更:靠写屏障记变更,标记时综合考量,准确遍历对象图,防存活对象漏判误判。
  • 数据竞争和一致性问题:在并发垃圾回收时,应用程序线程和垃圾回收线程可能同时访问和操作对象。例如,当垃圾回收器正在标记一个对象是否存活时,应用程序线程可能试图修改该对象的引用关系。通过锁机制,如互斥锁,可以确保在同一时刻只有一个线程能够访问和修改对象,避免数据竞争和不一致的情况发生,保证垃圾回收过程中对象状态的准确性,使得标记结果可靠,避免误判对象的存活状态。

通过这些方式,垃圾回收器能准确标记存活对象,为后续回收奠基。

5.什么是SATB(Snapshot At The Beginning,初始快照)?

  • 初始快照概念:SATB 即 "初始快照",它的核心思想是在垃圾回收的并发标记阶段开始时,对堆内存中的对象引用关系拍摄一个快照。在这个快照中记录下了当时对象之间的引用情况,后续的标记操作主要基于这个初始的快照来进行。
  • 记录引用变化:在并发标记过程中,应用程序线程持续运行,会不断创建新对象、修改对象间的引用关系或者删除引用。SATB 记录就是用来记录这些在并发阶段发生的对象引用变化的。通常借助写屏障(Write Barrier)技术来实现,当对象的引用关系被修改时,写屏障会记录下相关的信息,这些信息就构成了 SATB 记录。
  • 处理 SATB 记录原因如下:一是避免对象漏标记,防止因并发时引用关系变化,使存活对象未被标记而遭误回收致程序出错。二是维护标记一致性,将并发阶段引用变化纳入标记,准确反映对象真实存活状态。三是提升并发标记效率,使垃圾回收器在并发阶段持续工作,于最终标记阶段统一处理记录,避免频繁同步开销,保证标记正确。
  • 与TAMS 的协作关系:TAMS 主要负责对新创建对象的处理,从内存空间分配的角度辅助标记;而 SATB 侧重于记录对象引用关系的变化,从对象引用的角度保证标记的准确性。两者相互配合,使得 G1 垃圾回收器在并发环境下能够高效、准确地完成标记存活对象的任务。

【注:虽然并发标记阶段已经处理了部分并发操作对标记的影响,但最终标记阶段仍然需要处理 SATB 记录,因为并发标记阶段的处理难以实时跟踪全部变化,以及写屏障记录的延迟处理。】

【注:难以实时跟踪全部变化:虽然并发标记会尽力处理这些变化,但由于应用程序线程和垃圾回收线程同时运行,使得对象引用关系的变化十分频繁,很难实时且全面地跟踪所有对象引用关系的改变。例如,在极短的时间内,可能会有大量对象的引用关系被修改,并发标记过程中可能无法及时捕捉到所有这些变化。】

【注:写屏障记录的延迟处理:为了记录对象引用关系的变化,并发标记通常会使用写屏障技术。然而,写屏障只是记录下这些变化,并不会立即对标记结果进行更新。这些记录需要在后续阶段进行统一处理,以确保标记的准确性。】

6. 在 G1 垃圾回收器中,按回收收益排序 Region 的 "收益" 主要有

  • 可回收空间大小:即 Region 内垃圾对象占用空间。可回收空间大的 Region 能释放更多内存,减少碎片,回收收益高。
  • 回收成本:指回收 Region 时消耗的时间和资源。存活对象少的 Region 回收成本低,收益更高。
  • 对应用程序性能的影响:垃圾回收需暂停应用线程(STW),STW 时间短、对应用性能影响小的 Region 回收收益高。

G1 综合以上因素,优先回收可回收空间大、成本低且对应用性能影响小的 Region,提升垃圾回收效率与整体性能。

相关推荐
清水白石0082 小时前
协程不是线程:深入理解 Python async/await 运行机制
java·linux·python
程序员老乔2 小时前
Java 新纪元 — JDK 25 + Spring Boot 4 全栈实战(五):FFM API,告别JNI在Spring Boot中直连推荐引擎
java·开发语言·spring boot
va学弟2 小时前
Java 网络通信编程(7):完善视频通信
java·服务器·网络
fengpan20042 小时前
ubuntu下vscode使用串口
linux·运维·服务器
后青春期的诗go2 小时前
泛微OA-E9与第三方系统集成开发企业级实战记录(九)
java·金蝶·erp·泛微·oa·集成开发·e9
IMPYLH2 小时前
Linux 的 cut 命令
linux·运维·服务器·数据库
逸Y 仙X2 小时前
文章十:ElasticSearch索引字段高级属性
java·大数据·elasticsearch·搜索引擎·全文检索
阿贵---2 小时前
如何为开源Python项目做贡献?
jvm·数据库·python
我爱吃土豆12 小时前
HTTP首部讲解
后端·http·web