JDK的GC演进

JDK垃圾回收器的演进之路

一、各JDK版本的默认垃圾回收器

JDK 1.3及之前:Serial GC时代

默认回收器:Serial GC(串行回收器)

技术特点

  • 单线程回收:所有垃圾回收工作都在一个线程中完成
  • Stop-The-World:回收时会暂停所有用户线程
  • 简单可靠:实现简单,没有并发复杂性
  • 内存占用小:不需要额外的并发控制数据结构

适用场景

  • 单核CPU环境
  • 内存较小的应用(<100MB)
  • 桌面应用程序、客户端工具

配置参数-XX:+UseSerialGC

回收算法

  • 新生代:复制算法(Copying)
  • 老年代:标记-整理算法(Mark-Compact)

JDK 1.4-8:Parallel GC主导时期

默认回收器:Parallel GC(并行吞吐量优先回收器)

可选回收器

  • CMS(Concurrent Mark Sweep):通过 -XX:+UseConcMarkSweepGC 启用

技术突破

  • 多线程并行:充分利用多核CPU性能
  • 吞吐量优先:以最大化应用程序吞吐量为目标
  • 自适应调节:根据系统负载和硬件自动调整参数

适用场景

  • 多核CPU服务器
  • 批处理任务
  • 科学计算、数据处理
  • 对吞吐量要求高于响应时间的应用

关键配置参数

1
2
3
4
5
-XX:+UseParallelGC          # 启用Parallel GC(JDK 5-8默认)
-XX:+UseParallelOldGC # 启用老年代并行回收
-XX:ParallelGCThreads=8 # 设置GC线程数(通常等于CPU核心数)
-XX:MaxGCPauseMillis=200 # 最大GC停顿时间目标
-XX:GCTimeRatio=99 # GC时间占总时间比例(1/(1+99)=1%)

JDK 9-14:G1 GC成为默认

默认回收器:Garbage-First GC(G1垃圾回收器)

历史意义:JDK 9的重大变革,G1成为默认GC标志着GC技术进入新时代

核心特性

  • 分区化模型:将堆划分为多个独立的Region
  • 可预测停顿:可设定目标停顿时间
  • 增量回收:避免长时间的全堆回收
  • 空间整合:回收的同时进行内存整理

适用场景

  • 大内存应用(>4GB)
  • 要求延迟可控的服务器应用
  • 微服务、Web应用

关键参数

1
2
3
4
-XX:+UseG1GC                      # 启用G1 GC(JDK 9+默认)
-XX:MaxGCPauseMillis=200 # 目标最大停顿时间
-XX:G1HeapRegionSize=16m # Region大小
-XX:InitiatingHeapOccupancyPercent=45 # 触发并发标记的堆占用率

JDK 15-16:ZGC正式可用

新增回收器:Z Garbage Collector(ZGC)

技术特点

  • 超低延迟:目标停顿时间<1ms
  • 超大堆支持:支持TB级内存堆
  • 全并发处理:几乎所有工作都是并发执行
  • 染色指针:革命性的指针标记技术

配置启用-XX:+UseZGC

适用场景

  • 超大内存应用(>8GB)
  • 金融交易、实时竞价
  • 大数据处理、AI训练

JDK 17+:多元化GC选择

默认回收器:G1 GC继续作为默认选择

可选回收器

  • ZGC:生产就绪,稳定可用
  • Shenandoah:另一个低延迟GC选择
  • 分代ZGC:JDK 21引入分代ZGC(-XX:+ZGenerational

选择策略

  • 默认:G1 GC(平衡性能和稳定性)
  • 低延迟:ZGC
  • 超大堆:ZGC或分代ZGC

二、GC演进背后的驱动力与技术突破

GC技术演进全景图

graph TD
    A[Serial GC
JDK 1.3] -->|多核CPU普及| B[Parallel GC
JDK 1.4-8] B -->|互联网应用兴起
响应时间要求高| C[CMS GC
JDK 1.5+] C -->|内存碎片问题
大堆性能差| D[G1 GC
JDK 9+] D -->|金融/大数据应用
极致低延迟需求| E[ZGC/Shenandoah
JDK 15+] A1[单线程
Stop-The-World
适合小内存] --> A B1[多线程并行
吞吐量优先
适合批处理] --> B C1[并发标记清除
低延迟
适合Web应用] --> C D1[分区化
可预测停顿
适合大内存应用] --> D E1[全并发
超低延迟
适合超大内存/金融] --> E style A fill:#ffcccc style B fill:#ccffcc style C fill:#ccccff style D fill:#ffffcc style E fill:#ffccff

Serial → Parallel:拥抱多核时代

时代背景与硬件演进图

graph LR
    subgraph "硬件发展"
        H1[2000年
单核CPU] --> H2[2004年
双核CPU] --> H3[2006年
四核CPU] --> H4[2008年
八核CPU] end subgraph "GC技术演进" G1[Serial GC
单线程] --> G2[Parallel GC
多线程并行] end H3 --> G2 style H1 fill:#ffebcd style H2 fill:#ffebcd style H3 fill:#ffebcd style H4 fill:#ffebcd style G1 fill:#add8e6 style G2 fill:#98fb98

核心问题

  1. 资源浪费:在4核、8核服务器上,单线程GC只能利用1个CPU核心,其他核心闲置
  2. 吞吐量瓶颈:随着应用内存需求增长(从几十MB到几GB),单线程回收时间越来越长
  3. 应用性能下降:GC占用大量CPU时间,导致应用程序吞吐量下降

技术突破对比

graph TD
    subgraph "Serial GC"
        S1[单线程回收] --> S2[顺序处理]
        S3[CPU利用率低] --> S4[吞吐量受限]
    end

    subgraph "Parallel GC"
        P1[多线程并行] --> P2[同时处理]
        P3[CPU利用率高] --> P4[吞吐量大幅提升]
    end

    S1 -->|升级| P1
    S3 -->|解决| P3

    style S1 fill:#ffcccb
    style P1 fill:#90ee90

性能提升效果

1
2
3
4
5
6
7
8
9
10
11
12
8核服务器上的性能对比:
┌─────────────┬─────────────┬─────────────┐
│ GC类型 │ CPU利用率 │ 吞吐量提升 │
├─────────────┼─────────────┼─────────────┤
│ Serial GC │ 12.5% │ 1x (基准) │
│ Parallel GC │ 100% │ 2-4x │
└─────────────┴─────────────┴─────────────┘

实际应用场景:
- 批处理任务:数据处理速度提升3倍
- 科学计算:矩阵运算速度提升2.5倍
- 数据挖掘:算法执行时间缩短60%

Parallel → CMS:追求响应速度

时代需求与应用场景图

graph TD
    subgraph "互联网应用爆发"
        A[电商平台] --> B[在线支付]
        B --> C[社交媒体]
        C --> D[实时游戏]
    end

    subgraph "用户体验要求"
        E[响应时间<100ms] --> F[页面流畅度>60fps]
        F --> G[零容忍长时间卡顿]
    end

    D --> G

    style A fill:#ffebcd
    style B fill:#ffebcd
    style C fill:#ffebcd
    style D fill:#ffebcd
    style E fill:#add8e6
    style F fill:#add8e6
    style G fill:#ff6b6b

用户体验影响链

graph LR
    U[用户点击购买] --> GC[GC停顿3秒]
    GC --> PAGE[页面无响应]
    PAGE --> BAD[用户体验差]
    BAD --> LOSE[用户流失/订单损失]

    style GC fill:#ff6b6b
    style LOSE fill:#ff6b6b

GC工作模式对比

1
2
3
4
5
6
7
8
9
10
11
12
13
时间线对比 (单位:秒):

Parallel GC (传统方式):
[应用运行:3s]---[完全停顿:5s]---[应用运行:3s]
特点:用户在5秒内完全无法使用应用

CMS GC (并发方式):
[应用运行:3s]--[停顿:10ms]--[应用+GC并发:3s]--[停顿:20ms]--[应用运行:3s]
特点:大部分时间用户可以正常使用应用

优势对比:
- Parallel GC: 简单但用户体验差
- CMS GC: 复杂但用户体验好

CMS技术革新架构图

graph TD
    subgraph "CMS并发架构"
        A[应用线程] <-->|并发执行| B[GC线程]
        B --> C[并发标记]
        B --> D[并发清除]
        A --> E[短暂停顿点]
        E --> F[初始标记]
        E --> G[重新标记]
    end

    subgraph "传统GC架构"
        H[应用线程] -.->|完全停止| I[GC线程]
        I --> J[串行标记]
        I --> K[串行清除]
    end

    style A fill:#90ee90
    style B fill:#90ee90
    style C fill:#90ee90
    style D fill:#90ee90
    style H fill:#ffcccb
    style I fill:#ffcccb

技术挑战与解决方案

graph LR
    subgraph "挑战"
        T1[线程协调复杂]
        T2[内存动态变化]
        T3[CPU资源竞争]
    end

    subgraph "解决方案"
        S1[写屏障技术]
        S2[三色标记法]
        S3[自适应调度]
    end

    T1 --> S1
    T2 --> S2
    T3 --> S3

    style T1 fill:#ffcccb
    style T2 fill:#ffcccb
    style T3 fill:#ffcccb
    style S1 fill:#90ee90
    style S2 fill:#90ee90
    style S3 fill:#90ee90

CMS → G1:兼顾延迟与大堆

CMS致命缺陷问题树

graph TD
    A[CMS GC缺陷] --> B[内存碎片化]
    A --> C[并发模式失败]
    A --> D[大堆性能恶化]

    B --> B1[标记不整理]
    B1 --> B2[内存支离破碎]
    B2 --> B3[无法分配大对象]
    B3 --> B4[频繁Full GC]

    C --> C1[标记期间空间不足]
    C1 --> C2[退化到Serial Old]
    C2 --> C3[停顿时间更长]

    D --> D1[4GB堆: 2-3秒]
    D1 --> D2[8GB堆: 5-8秒]
    D2 --> D3[16GB堆: 10-15秒]
    D3 --> D4[基本不可用]

    style A fill:#ff6b6b
    style B4 fill:#ff6b6b
    style C3 fill:#ff6b6b
    style D4 fill:#ff6b6b

内存碎片问题可视化

graph LR
    subgraph "CMS内存碎片"
        M1[已用] --> M2[空闲
100KB] M2 --> M3[已用] --> M4[空闲
50KB] M4 --> M5[已用] --> M6[空闲
200KB] M6 --> M7[已用] end subgraph "分配需求" NEED[需要连续2MB空间] end subgraph "结果" FAIL[分配失败 → Full GC → 长时间停顿] end M6 --> FAIL style NEED fill:#ffeb3b style FAIL fill:#ff6b6b

G1革命性设计架构图

graph TD
    subgraph "传统分代内存"
        T1[固定Eden区]
        T2[固定Survivor区]
        T3[固定老年代]
    end

    subgraph "G1分区内存"
        G1[Region1: Eden]
        G2[Region2: Eden]
        G3[Region3: Survivor]
        G4[Region4: Old]
        G5[Region5: Old]
        G6[Region6: Humongous]
        G7[Region7: Free]
    end

    T3 -->|升级为| G1
    T3 -->|升级为| G2
    T3 -->|升级为| G3

    style T1 fill:#ffcccb
    style T2 fill:#ffcccb
    style T3 fill:#ffcccb
    style G1 fill:#90ee90
    style G2 fill:#90ee90
    style G3 fill:#90ee90
    style G4 fill:#90ee90
    style G5 fill:#90ee90
    style G6 fill:#90ee90
    style G7 fill:#90ee90

G1智能回收决策流程

flowchart TD
    START[GC触发] --> CHECK{检查堆占用率}
    CHECK -->|>45%| MARK[并发标记周期]
    CHECK -->|<45%| YOUNG[年轻代GC]

    MARK --> PREDICT[预测停顿时间]
    PREDICT --> SELECT[选择回收Region]
    SELECT --> CALC{预计停顿时间}

    CALC -->|<200ms| EXECUTE[执行混合GC]
    CALC -->|>200ms| ADJUST[减少Region数量]
    ADJUST --> EXECUTE

    EXECUTE --> END[GC完成]

    style START fill:#e1f5fe
    style EXECUTE fill:#c8e6c9
    style END fill:#c8e6c9

G1性能优势对比表

graph LR
    subgraph "堆大小 vs 停顿时间"
        H1[4GB] --> S1[停顿: 50-100ms]
        H2[8GB] --> S2[停顿: 100-150ms]
        H3[16GB] --> S3[停顿: 150-200ms]
        H4[32GB] --> S4[停顿: 200ms]
    end

    subgraph "适用场景"
        A[微服务架构]
        B[大内存Web应用]
        C[实时数据处理]
    end

    S3 --> A
    S3 --> B
    S4 --> C

    style S1 fill:#c8e6c9
    style S2 fill:#c8e6c9
    style S3 fill:#c8e6c9
    style S4 fill:#c8e6c9

G1 → ZGC:迈向极致延迟

现代应用场景需求图

graph TD
    subgraph "金融交易场景"
        F1[CPU处理: 0.1ms] --> F2[网络传输: 0.5ms]
        F2 --> F3[G1 GC停顿: 50ms]
        F3 --> F4[总延迟: 50.6ms]
        F4 --> F5[GC占比: 98.8%]
    end

    subgraph "大数据AI场景"
        A1[数据集: 100GB] --> A2[模型参数: 50GB]
        A2 --> A3[内存堆: 512GB]
        A3 --> A4[G1停顿: 500ms-2s]
        A4 --> A5[训练效率低下]
    end

    style F3 fill:#ff6b6b
    style F5 fill:#ff6b6b
    style A4 fill:#ff6b6b
    style A5 fill:#ff6b6b

ZGC技术突破架构图

graph TD
    subgraph "ZGC核心技术"
        C1[染色指针技术]
        C2[读屏障机制]
        C3[全并发设计]
        C4[多级内存管理]
    end

    subgraph "实现效果"
        E1[停顿<1ms]
        E2[TB级堆支持]
        E3[并发对象移动]
        E4[内存开销极低]
    end

    C1 --> E1
    C1 --> E4
    C2 --> E1
    C2 --> E3
    C3 --> E1
    C4 --> E2

    style C1 fill:#e1f5fe
    style C2 fill:#e1f5fe
    style C3 fill:#e1f5fe
    style C4 fill:#e1f5fe
    style E1 fill:#c8e6c9
    style E2 fill:#c8e6c9
    style E3 fill:#c8e6c9
    style E4 fill:#c8e6c9

染色指针技术详解

graph LR
    subgraph "传统64位指针"
        T1[63-48: 保留位] --> T2[47-0: 对象地址]
    end

    subgraph "ZGC染色指针"
        Z1[63-42: 对象地址] --> Z2[41-38: 颜色位] --> Z3[37-0: 页内偏移]
    end

    Z2 --> Z2A[00: 未标记]
    Z2 --> Z2B[01: 已标记]
    Z2 --> Z2C[10: 重定位中]
    Z2 --> Z2D[11: 重定位完成]

    style T1 fill:#ffcccb
    style Z1 fill:#c8e6c9
    style Z2 fill:#ffeb3b
    style Z3 fill:#c8e6c9

ZGC全并发工作流程

1
2
3
4
5
6
7
8
9
10
时间线 (单位:毫秒):

应用线程: [================应用运行=================]
:
ZGC线程: [STW:1ms][并发标记:3000ms][并发重定位:4000ms][STW:1ms]

关键点:
- 停顿时间:<1ms (用户几乎无感知)
- 并发工作:99.9%的GC工作与应用并发执行
- 堆大小不影响停顿时间

读屏障工作机制

sequenceDiagram
    participant App as 应用线程
    participant Heap as 堆内存
    participant ZGC as ZGC系统

    App->>Heap: 读取对象引用
    Heap->>ZGC: 检查指针颜色位
    alt 对象正在移动
        ZGC->>Heap: 获取新位置
        Heap->>App: 返回新地址
    else 对象已稳定
        ZGC->>App: 直接返回地址
    end
    App->>App: 继续执行

性能扩展性对比

graph LR
    subgraph "G1 GC"
        G1_8[8GB堆: 100ms] --> G1_32[32GB堆: 400ms]
        G1_32 --> G1_128[128GB堆: 1600ms]
        G1_128 --> G1_512[512GB堆: 6400ms]
    end

    subgraph "ZGC"
        Z1_8[8GB堆: <1ms] --> Z1_32[32GB堆: <1ms]
        Z1_32 --> Z1_128[128GB堆: <1ms]
        Z1_128 --> Z1_512[512GB堆: <1ms]
    end

    style G1_512 fill:#ff6b6b
    style Z1_512 fill:#c8e6c9

最终GC选择决策树

flowchart TD
    START[选择GC] --> HEAP{堆大小}

    HEAP -->|<100MB| SMALL[小内存应用]
    HEAP -->|100MB-4GB| MEDIUM[中等内存应用]
    HEAP -->|4GB-32GB| LARGE[大内存应用]
    HEAP -->|>32GB| XLARGE[超大内存应用]

    SMALL --> CLIENT{应用类型}
    CLIENT -->|客户端| SERIAL[Serial GC]
    CLIENT -->|服务端| PARALLEL[Parallel GC]

    MEDIUM --> REQUIREMENT{性能要求}
    REQUIREMENT -->|吞吐量优先| PARALLEL2[Parallel GC]
    REQUIREMENT -->|延迟敏感| G1_SMALL[G1 GC]

    LARGE --> DELAY{延迟要求}
    DELAY -->|可接受100-200ms| G1_LARGE[G1 GC]
    DELAY -->|要求<10ms| ZGC_LARGE[ZGC]

    XLARGE --> FINANCIAL{金融场景?}
    FINANCIAL -->|是| ZGC_FINAL[ZGC]
    FINANCIAL -->|否| ZGC_BIG[ZGC]

    style SERIAL fill:#add8e6
    style PARALLEL fill:#90ee90
    style G1_SMALL fill:#ffeb3b
    style G1_LARGE fill:#ffeb3b
    style ZGC_LARGE fill:#ff6b6b
    style ZGC_FINAL fill:#ff6b6b
    style ZGC_BIG fill:#ff6b6b

三、垃圾收集的核心技术演进

在了解GC演进历程后,让我们深入理解驱动这些演进的核心技术变革。

3.1 可达性分析算法:从串行到并发

可达性分析基本原理

1
2
3
4
可达性分析原理:
GC Roots(起点) → 引用链 → 对象图遍历 → 存活对象集合
↓ ↓ ↓ ↓
绝对不能回收 通过引用可达 递归查找 标记为存活

不同GC的可达性分析策略

graph TD
    subgraph "串行分析"
        S1[单线程遍历] --> S2[全程STW] --> S3[简单但低效]
    end

    subgraph "并行分析"
        P1[多线程遍历] --> P2[缩短STW] --> P3[提高效率]
    end

    subgraph "并发分析"
        C1[三色标记] --> C2[部分STW] --> C3[复杂但高效]
    end

    style S1 fill:#ffcccb
    style P1 fill:#90ee90
    style C1 fill:#ffeb3b

3.2 三色标记法:并发标记的理论基础

三色标记原理

1
2
3
4
三色状态:
● 白色:未被标记(垃圾对象)
○ 灰色:已标记但引用未完全处理(正在处理)
● 黑色:已标记且引用已处理(存活对象)

标记过程流程

flowchart TD
    START[开始标记] --> SCAN[扫描GC Roots]
    SCAN --> COLOR[将Root对象标记为灰色]
    COLOR --> GRAY_QUEUE[加入灰色队列]

    GRAY_QUEUE --> CHECK{灰色队列是否为空?}
    CHECK -->|否| TAKE_GRAY[取出一个灰色对象]
    TAKE_GRAY --> SCAN_REFS[扫描对象的所有引用]
    SCAN_REFS --> CHECK_REF{引用的对象状态}

    CHECK_REF -->|白色| MARK_GRAY[标记为灰色并加入队列]
    CHECK_REF -->|灰色/黑色| IGNORE[已标记,忽略]

    MARK_GRAY --> GRAY_QUEUE
    IGNORE --> MARK_BLACK[当前对象标记为黑色]
    MARK_BLACK --> CHECK

    CHECK -->|是| COMPLETE[标记完成,白色对象为垃圾]

    style START fill:#e1f5fe
    style COMPLETE fill:#c8e6c9
    style SCAN fill:#bbdefb
    style COLOR fill:#64b5f6
    style MARK_GRAY fill:#42a5f5
    style MARK_BLACK fill:#2196f3

三色标记状态转换图

stateDiagram-v2
    [*] --> 白色: 新创建对象
    白色 --> 灰色: 被灰色对象引用
    灰色 --> 黑色: 引用全部扫描完成
    白色 --> 黑色: 直接被Root引用
    灰色 --> 灰色: 发现新的白色引用

    黑色 --> [*]: 垃圾回收时清除

3.3 写屏障:处理并发过程中的引用变化

写屏障的作用

1
2
3
4
5
6
7
// 伪代码:写屏障示例
void write_barrier(Object obj, Object new_value) {
// 在引用更新前后的处理
pre_write_barrier(obj);
obj.field = new_value; // 实际写操作
post_write_barrier(obj, new_value);
}

不同GC的写屏障策略

  • SATB(Snapshot-At-The-Beginning):G1 GC使用
  • 增量更新(Incremental Update):CMS GC使用
  • 读屏障:ZGC使用(更复杂但效果更好)

3.4 内存回收算法的演进

算法发展历程

graph LR
    subgraph "早期算法"
        A1[标记-清除] --> A2[复制算法] --> A3[标记-整理]
    end

    subgraph "现代算法"
        B1[分区回收] --> B2[增量回收] --> B3[并发回收]
    end

    A3 --> B1

    style A1 fill:#add8e6
    style B3 fill:#90ee90

四、内存管理模型的演进:从分代到分区

G1是垃圾收集器发展的分水岭,它彻底改变了内存管理方式。理解这一变革对掌握现代GC技术至关重要。

4.1 传统分代模型(Serial, Parallel, CMS)

物理内存布局

1
2
3
4
5
6
7
8
9
传统分代内存结构:
┌─────────────────────────────────────────────────┐
│ Java堆内存 │
├─────────────────┬───────────────────────────────┤
│ 新生代 │ 老年代 │
├─────────────────┼───────────────────────────────┤
│ Eden │ S0 │ S1│ 老年代区域 │
│ 80% │ 10% │10%│ 固定大小 │
└───────┴──────┴─────┴─────────────────────────────┘

分代理论基础

  1. 弱分代假说:绝大多数对象都是”朝生夕死”的(生命周期短)
  2. 强分代假说:熬过的GC次数越多,对象越难以消亡
  3. 跨代引用假说:跨代引用相对同代引用极少

回收策略特点

1
2
3
4
5
6
7
8
9
10
11
新生代回收(Minor GC):
- 频率:高(几分钟一次)
- 算法:复制算法
- 停顿:短(几十毫秒)
- 对象:大部分是垃圾

老年代回收(Major GC):
- 频率:低(几小时一次)
- 算法:标记-清除/标记-整理
- 停顿:长(几百毫秒到几秒)
- 对象:大部分存活

固有局限性

  1. 刚性边界:Eden、Survivor、Old区域大小固定,无法动态调整
  2. 内存碎片:CMS的标记-清除算法产生大量碎片
  3. 大对象困境:大对象直接进入老年代,可能触发频繁Full GC
  4. 粗粒度回收:老年代只能全量回收,无法精细化控制

4.2 分区模型(G1, ZGC)

逻辑内存布局

1
2
3
4
5
6
7
8
G1分区内存结构:
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ R1 │ R2 │ R3 │ R4 │ R5 │ R6 │ R7 │
│ Eden │ Eden │Surv │ Old │ Old │Humong│Free │
│ (2M) │ (2M) │(1M) │ (4M) │ (4M)│ (8M) │ (2M) │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘

每个Region都是独立的"小区间",可以灵活转换角色

核心设计理念

Region的基本特性
  • 统一大小:每个Region大小相等(通常1-32MB,JVM自动选择)
  • 角色可变:同一个Region可以是Eden、Survivor、Old等角色
  • 独立回收:以Region为单位进行垃圾回收
  • 动态调整:根据应用需求动态分配Region角色

2. G1的智能回收策略

1
2
3
4
5
6
7
8
Garbage-First原理:
┌─────────────────────────────────────────────────┐
│ 80%垃圾 │ 90%垃圾 │ 20%垃圾 │ 95%垃圾 │ 10%垃圾 │
└─────────────────────────────────────────────────┘
↑ ↑ ↑
优先级1 优先级2 优先级N

优先回收垃圾最多的Region,效率最高!

3. 停顿时间控制

1
2
3
4
5
目标:每次GC停顿不超过200ms
策略:
- 估算每个Region的回收时间
- 选择能在200ms内完成的Region集合
- 优先选择垃圾比例高的Region

4. 大对象专门处理

  • Humongous Region:超过Region大小50%的对象分配到这里
  • 连续空间:大对象可能占用多个连续Region
  • 特殊回收:大对象通常在Full GC时处理

分区模型的优势

1. 灵活性革命

1
2
3
4
5
6
7
8
9
传统分代:刚性边界,无法调整
┌─────────┬─────────┬─────────┐
│ Eden:固定│ Old:固定 │Surv:固定│
└─────────┴─────────┴─────────┘

分区模型:动态边界,按需分配
┌────┬────┬────┬────┬────┬────┬────┐
│Eden│Eden│Surv│ Old│ Old│Humon│Free│
└────┴────┴────┴────┴────┴────┴────┘

2. 精细化控制

  • 选择性回收:只回收需要的Region
  • 增量式整理:局部整理,避免全局停顿
  • 预测性调度:根据历史数据预测GC时间

3. 大堆友好

  • 线性扩展:堆大小增长不影响单次GC停顿时间
  • TB级支持:支持超大内存堆(理论上无限制)

4.3 ZGC的进一步演进

多级Page设计

1
2
3
4
5
6
ZGC内存管理:
Small Page (2MB) Medium Page (32MB) Large Page (>32MB)
┌───────────────┐ ┌─────────────────┐ ┌─────────────────────┐
│ 小对象<256KB │ │ 中等对象<32MB │ │ 大对象>=32MB │
│ 复制算法 │ │ 复制算法 │ │ 标记清除(不移动) │
└───────────────┘ └─────────────────┘ └─────────────────────┘

全并发特性

  • 无分代设计(JDK 21前):所有对象统一管理
  • 染色指针:对象状态存储在指针中
  • 读屏障:处理并发访问问题

对比总结

1
2
3
4
5
6
7
特性对比          │ G1              │ ZGC
─────────────────┼─────────────────┼─────────────────
分区大小 │ 1-32MB │ 2MB/32MB/Large
分代支持 │ 是 │ 否(JDK 21+支持)
停顿目标 │ 100-200ms │ <1ms
堆大小限制 │ 约100GB │ TB级
实现复杂度 │ 高 | 极高


五、各垃圾回收器的工作原理与关键技术

5.1 Serial GC 串行垃圾回收器

运行步骤

Serial GC - 单线程串行垃圾回收器

新生代回收(Minor GC)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
工作流程:

1. 暂停应用线程(STW - Stop The World)
- 所有运行的应用线程到达安全点
- 线程状态被保存
- 应用程序完全暂停

2. 识别和标记存活对象
- 从GC Roots开始扫描
- 检查栈中的局部变量、静态变量等根对象
- 标记直接引用的对象为存活
- 递归标记所有可达对象

3. 复制存活对象
- 准备空的Survivor空间
- 将Eden区存活对象复制到Survivor区
- 将Survivor区存活对象复制到另一个Survivor区
- 对象年龄+1
- 达到年龄阈值的对象晋升到老年代

4. 清理内存空间
- 清空Eden区
- 清空From Survivor区
- 重置内存分配指针
- 交换Survivor区角色

5. 恢复应用线程
- 重置GC相关数据结构
- 更新GC统计信息
- 唤醒所有暂停的应用线程
- 应用程序从安全点继续执行

特点:
- 单线程执行,简单可靠
- 复制算法保证无内存碎片
- 停顿时间较长,影响响应性能

老年代回收(Full GC)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
工作流程:

1. 暂停应用线程(STW)
- 所有应用线程停止运行
- 获取整个堆的访问权
- 准备进行完整清理

2. 标记存活对象
- 遍历所有GC Roots
- 标记直接引用的老年代对象
- 递归标记所有可达对象
- 构建完整的存活对象图

3. 整理内存空间
- 计算对象的移动目标位置
- 移动存活对象到新位置
- 更新所有引用指向新地址
- 消除内存碎片

4. 清理垃圾对象
- 回收未被标记对象的内存
- 合并连续的空闲内存块
- 更新空闲内存列表
- 优化内存布局

5. 恢复应用线程
- 重置GC数据结构
- 清理标记信息
- 唤醒应用线程
- 应用恢复正常运行

特点:
- 标记-整理算法消除内存碎片
- 停顿时间长,影响用户体验
- 适合单核CPU或小内存应用

关键技术

  • 复制算法:新生代使用,简单高效,无碎片
  • 标记-整理算法:老年代使用,解决碎片问题
  • 单线程执行:所有GC操作都在一个线程中完成

优缺点

优点

  • 实现简单,内存占用小
  • 单线程避免多线程竞争
  • 适合单核CPU环境

缺点

  • STW时间长,影响用户体验
  • 无法利用多核CPU优势
  • 回收效率低

5.2 Parallel GC 并行垃圾回收器

运行步骤

Parallel GC - 多线程并行垃圾回收器

新生代并行回收(Parallel Scavenge)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
工作流程:

1. 暂停应用线程(STW)
- 所有应用线程到达安全点
- 线程状态保存
- 应用程序暂停

2. 初始化并行GC
- 创建多个GC线程(数量=CPU核心数)
- 分配线程工作队列
- 初始化线程同步机制
- 将内存区域划分为多个部分

3. 并行根扫描
- 多个GC线程同时扫描GC Roots
- 线程1扫描虚拟机栈
- 线程2扫描方法区
- 线程3扫描JNI引用
- 其他线程扫描其他根对象
- 动态负载均衡,避免线程空闲

4. 并行标记与复制
- 多线程同时遍历对象引用图
- 并行处理Eden区和Survivor区的对象
- 复制存活对象到新的Survivor区
- 更新对象引用关系
- 处理对象晋升到老年代

5. 并行清理
- 多线程同时清理内存区域
- 清空Eden区和From Survivor区
- 重置内存分配指针
- 更新内存管理数据结构

6. 自适应参数调整
- 分析GC性能数据
- 动态调整Eden/Survivor比例
- 优化对象晋升年龄阈值
- 调整GC线程数量

7. 恢复应用线程
- 所有GC线程完成工作
- 唤醒应用线程
- 应用程序继续运行

特点:
- 多线程并行执行,充分利用多核CPU
- 工作负载均衡,动态调整任务分配
- 自适应调节参数,优化性能
- 吞吐量优先,适合批处理任务

老年代并行回收(Parallel Old)

Parallel Old GC执行步骤

  1. 初始标记阶段(STW)

    • 暂停应用线程
    • 多线程并行标记GC Roots直接引用的对象
    • 构建初始标记集合
  2. 并行标记阶段

    • 多个GC线程并行遍历对象图
    • 标记所有存活对象
    • 动态负载均衡
  3. 并行整理阶段

    • 多线程并行整理内存空间
    • 移动存活对象,消除内存碎片
    • 更新对象引用
  4. 并行清理阶段

    • 多线程并行回收垃圾对象
    • 合并空闲内存空间
    • 更新内存分配器
  5. 恢复应用线程

    • 重置GC数据结构
    • 唤醒应用线程
    • 恢复正常运行

特点:

  • 多线程并行执行,提高回收效率
  • 适合多核CPU环境
  • 吞吐量优先,适合批处理任务

关键技术

  • 自适应调节:根据运行情况自动调整GC参数
  • 吞吐量优先:优化总计算时间中GC时间的比例
  • 并行处理:充分利用多核CPU能力

优缺点

优点

  • 高吞吐量,适合后台计算
  • 充分利用多核CPU
  • 自适应调节减少手动调优

缺点

  • STW仍然较长
  • 对延迟敏感的应用不友好
  • GC线程可能与应用线程竞争CPU

5.3 CMS GC 并发标记清除垃圾回收器

运行步骤

CMS GC - 并发标记清除垃圾回收器

CMS回收过程(主要针对老年代)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
1. 初始标记阶段(短暂STW)
- 触发条件:老年代使用率达到68%或System.gc()调用
- 暂停应用线程
- 快速扫描GC Roots直接引用的对象
- 标记为存活对象
- 恢复应用线程运行

2. 并发标记阶段(应用线程正常运行)
- 从初始标记的对象开始遍历
- 并发标记所有可达对象
- 处理应用线程产生的引用变更
- 使用增量更新写屏障记录变化

3. 预清理阶段(并发执行)
- 处理并发标记期间的新引用变更
- 减少重新标记阶段的工作量
- 为最终标记做准备

4. 重新标记阶段(STW)
- 再次暂停应用线程
- 处理写屏障记录的引用变更
- 完成最终标记确认
- 确保标记准确性

5. 并发清除阶段(应用线程正常运行)
- 并发清除未被标记的垃圾对象
- 回收内存空间
- 更新空闲内存列表
- 处理引用队列

6. 并发重置阶段(并发执行)
- 重置CMS数据结构
- 清理标记位图
- 为下次GC做准备

特点:
- 大部分时间并发执行,减少停顿时间
- 适用于响应时间要求高的应用
- 产生内存碎片,需要定期Full GC整理


#### 关键技术
- **并发标记清除**:大部分工作与应用并发执行
- **增量更新写屏障**:处理并发过程中的引用变化
- **卡片表**:加速跨代引用扫描

#### 优缺点
**优点**:
- 并发执行,减少STW时间
- 响应时间好,适合Web应用
- 老年代回收停顿时间短

**缺点**:
- 产生内存碎片
- 并发模式失败可能退化到串行GC
- CPU敏感,需要额外CPU资源
- 浮动垃圾问题

---

### 5.4 G1 GC 垃圾优先垃圾回收器

#### 运行步骤

**G1 GC执行步骤**:

1. 年轻代回收(Young GC)
- Eden区满时触发
- 暂停应用线程(短暂STW)
- 复制存活对象到Survivor区
- 处理对象晋升
- 更新Remembered Set
- 转换Region角色
- 控制停顿时间

2. 并发标记周期
- 堆使用率达到45%时触发
- 初始标记(伴随Young GC)
- 根区域扫描(并发)
- 并发标记(并发)
- 重新标记(STW)
- 独占清理(STW)
- 并发清理(并发)
3. 混合回收(Mixed GC)
- 优先回收垃圾最多的Region
- 同时处理年轻代和老年代
- 控制停顿时间在目标范围内
- 多次执行直到回收足够空间

4. Full GC(最后手段)
- 混合回收无法释放足够空间时触发
- 串行整理整个堆
- 消除内存碎片
- 停顿时间较长

特点:
- 分区管理,灵活控制
- 可预测的停顿时间
- 增量式回收
- 适合大内存应用

#### 关键技术
- **分区模型**:将堆划分为多个Region
- **Remembered Set**:处理跨Region引用
- **SATB写屏障**:保证并发标记准确性
- **停顿预测模型**:控制GC停顿时间
- **增量回收**:逐步回收,避免长时间停顿

#### 优缺点
**优点**:
- 停顿时间可预测且可控
- 支持大内存堆(8GB+)
- 增量回收,减少STW时间
- 内存利用率高,减少碎片

**缺点**:
- 需要额外的记忆集开销
- 在小堆上可能不如其他GC
- 实现复杂,调优难度较高

---

### 5.5 ZGC Z垃圾回收器

#### 运行步骤

**ZGC执行步骤**:

1. 标记根阶段(极短STW,通常<1ms)
- 快速扫描GC Roots
- 使用染色指针标记对象
- 应用几乎无感知

2. 并发标记阶段
- 遍历对象图标记所有存活对象
- 使用读屏障处理引用变更
- 应用线程正常运行

3. 并发转移准备阶段
- 识别需要转移的对象集合
- 选择转移目标页面
- 准备转移空间

4. 转移根阶段(极短STW)
- 更新GC Roots中的引用
- 设置转发指针
- 应用几乎无感知

5. 并发转移阶段
- 并发转移存活对象到新位置
- 使用读屏障处理对象访问
- 应用线程正常运行
特点:
- 极低延迟(停顿时间<1ms)
- 完全并发执行
- 支持超大内存堆(TB级别)
- 适用于延迟敏感的实时应用

#### 关键技术详解

##### 1. 染色指针技术(Colored Pointers)

64位指针布局:
[63-48] [47-42] [41-0]
保留位 颜色位 对象地址

颜色位含义:

  • 00:未标记/未迁移
  • 01:已标记
  • 10:已迁移
  • 11:最终可达
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    ##### 2. 读屏障(Load Barrier)
    ```java
    // 伪代码:ZGC读屏障
    Object load(Object* field) {
    Object obj = *field; // 读取引用

    // 检查对象状态并进行相应处理
    if (is_relocating(obj)) {
    obj = forward_pointer(obj); // 更新引用
    }

    return obj;
    }

ZGC的Page结构

1
2
3
4
5
6
7
8
9
10
11
12
Page层级结构:
Small Page:2MB
├── 适合小对象
└── 复制算法

Medium Page:32MB
├── 适合中等大小对象
└── 复制算法

Large Page:>32MB
├── 适合大对象
└── 不移动,原地标记清理

优缺点

优点

  • 超低延迟(<1ms STW)
  • 支持超大内存堆(TB级)
  • 停顿时间不随堆大小增长
  • 全并发,充分利用CPU

缺点

  • 实现极其复杂
  • 读屏障有一定性能开销
  • 需要64位系统支持
  • JDK 15+才稳定可用

六、各垃圾回收器工作流程对比分析

6.1 GC工作流程时间线对比

不同GC的停顿时间对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
时间线对比 (单位:毫秒):

Serial GC:
[应用运行 3000ms]---[STW停顿 2000ms]---[应用运行 3000ms]

Parallel GC:
[应用运行 3000ms]---[并行回收 1000ms]---[应用运行 3000ms]

CMS GC:
[应用运行 3000ms]--[STW 10ms]--[并发标记 3000ms]--[STW 20ms]--[应用运行 3000ms]

G1 GC:
[应用运行 3000ms]--[Young GC 100ms]--[应用运行 3000ms]--[Mixed GC 150ms]--[应用运行]

ZGC:
[应用运行 5000ms]--[STW 1ms]--[应用运行 5000ms]--[STW 1ms]--[应用运行]

6.2 关键性能指标对比

GC类型 典型停顿时间 并发程度 适用场景
Serial 2000-5000ms ❌ 无并发 客户端应用
Parallel 1000-2000ms ❌ 无并发 批处理任务
CMS 10-50ms ✅ 部分并发 Web应用
G1 100-200ms ✅ 部分并发 大内存应用
ZGC <1ms ✅ 全并发 金融/大数据

6.3 GC特性对比矩阵

GC类型 吞吐量 延迟 内存占用 复杂度 稳定性
Serial GC ⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Parallel GC ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
CMS GC ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐
G1 GC ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐
ZGC ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐

特性说明

  • ⭐⭐⭐⭐⭐ = 优秀(高吞吐量/低延迟/低占用等)
  • ⭐⭐⭐⭐ = 良好
  • ⭐⭐⭐ = 中等
  • ⭐⭐ = 较差
  • ⭐ = 差

6.4 关键技术对比表

GC类型 关键技术 并发程度 停顿时间 适用场景
Serial 单线程STW ❌ 无 100-1000ms 客户端应用
Parallel 多线程并行 ❌ 无 50-200ms 批处理任务
CMS 并发标记清除 ✅ 部分 10-50ms Web应用
G1 分区化内存 ✅ 部分 10-100ms 大内存应用
ZGC 染色指针 ✅ 全量 <1ms 金融/大数据

6.5 内存管理策略对比

graph TD
    subgraph "内存模型演进"
        M1[固定分代] --> M2[分区化] --> M3[全并发]
    end

    subgraph "Serial/Parallel"
        F1[Eden + 2Survivor + Old]
        F2[固定边界]
        F3[复制+标记整理]
    end

    subgraph "G1"
        R1[多个Region]
        R2[动态角色]
        R3[增量回收]
    end

    subgraph "ZGC"
        Z1[Small+Medium+Large Page]
        Z2[染色指针]
        Z3[全并发移动]
    end

    M1 --> F1
    M2 --> R1
    M3 --> Z1

    style F1 fill:#add8e6
    style R1 fill:#90ee90
    style Z1 fill:#ff6b6b

6.6 GC性能指标详细对比

Serial GC

  • 吞吐量: ⭐⭐ (单线程限制,适合轻负载)
  • 延迟: ⭐⭐ (STW时间长,不适合交互应用)
  • 内存占用: ⭐⭐⭐⭐⭐ (最简单的实现,内存开销最小)
  • 实现复杂度: ⭐⭐⭐⭐⭐ (最简单,稳定可靠)
  • 稳定性: ⭐⭐⭐⭐⭐ (经过最长时间验证)
Parallel GC
  • 吞吐量: ⭐⭐⭐⭐⭐ (多核并行,吞吐量最高)
  • 延迟: ⭐⭐ (STW仍然较长)
  • 内存占用: ⭐⭐⭐⭐ (中等开销)
  • 实现复杂度: ⭐⭐⭐⭐ (相对简单)
  • 稳定性: ⭐⭐⭐⭐ (成熟稳定)

CMS GC

  • 吞吐量: ⭐⭐⭐ (并发执行,但CPU开销大)
  • 延迟: ⭐⭐⭐⭐ (低延迟,但有波动)
  • 内存占用: ⭐⭐⭐ (需要额外数据结构)
  • 实现复杂度: ⭐⭐ (并发算法复杂)
  • 稳定性: ⭐⭐ (存在并发模式失败风险)

G1 GC

  • 吞吐量: ⭐⭐⭐⭐ (平衡性能,略有开销)
  • 延迟: ⭐⭐⭐⭐ (可预测延迟,可控)
  • 内存占用: ⭐⭐⭐ (Remembered Set开销)
  • 实现复杂度: ⭐⭐ (分区管理复杂)
  • 稳定性: ⭐⭐⭐⭐ (经过长期验证)

ZGC

  • 吞吐量: ⭐⭐⭐⭐ (接近G1,略有读屏障开销)
  • 延迟: ⭐⭐⭐⭐⭐ (超低延迟,<1ms)
  • 内存占用: ⭐⭐ (多映射内存开销较大)
  • 实现复杂度: ⭐ (最复杂的技术实现)
  • 稳定性: ⭐⭐⭐ (相对较新,持续优化)

七、GC选择策略与最佳实践

7.1 GC选择决策流程

flowchart TD
    START[选择GC] --> MEMORY{内存大小}

    MEMORY -->|<100MB| SMALL1[小内存应用]
    MEMORY -->|100MB-2GB| MEDIUM1[中等内存应用]
    MEMORY -->|2GB-16GB| LARGE1[大内存应用]
    MEMORY -->|>16GB| XLARGE1[超大内存应用]

    SMALL1 --> SMALL_TYPE{应用类型}
    SMALL_TYPE -->|客户端工具| SERIAL[Serial GC]
    SMALL_TYPE -->|服务端| PARALLEL[Parallel GC]

    MEDIUM1 --> MEDIUM_REQ{性能要求}
    MEDIUM_REQ -->|吞吐量优先| PARALLEL2[Parallel GC]
    MEDIUM_REQ -->|延迟敏感| CMS[CMS GC]

    LARGE1 --> LARGE_DELAY{延迟要求}
    LARGE_DELAY -->|可接受100ms| G1[G1 GC]
    LARGE_DELAY -->|要求<50ms| G1_LATENCY[优化配置的G1]

    XLARGE1 --> XLARGE_SCENE{应用场景}
    XLARGE_SCENE -->|金融交易| ZGC[ZGC]
    XLARGE_SCENE -->|大数据AI| ZGC_BIG[ZGC]
    XLARGE_SCENE -->|普通应用| G1_BIG[大内存G1]

    style SERIAL fill:#add8e6
    style PARALLEL fill:#90ee90
    style CMS fill:#ffeb3b
    style G1 fill:#90ee90
    style ZGC fill:#ff6b6b

7.2 推荐配置参数

客户端应用(小内存)

1
-XX:+UseSerialGC -Xms128m -Xmx512m

批处理任务(高吞吐量)

1
-XX:+UseParallelGC -Xms2g -Xmx4g -XX:ParallelGCThreads=4

Web应用(平衡性能)

1
-XX:+UseG1GC -Xms4g -Xmx8g -XX:MaxGCPauseMillis=200

大数据应用(低延迟)

1
-XX:+UseZGC -Xms16g -Xmx32g

八、总结与展望

8.1 GC技术演进总结

从JDK 1.3的Serial GC到JDK 17+的ZGC,Java垃圾收集器经历了一个不断优化的演进过程:

  1. 单线程时代(JDK 1.3):简单但低效的串行收集
  2. 并行时代(JDK 1.4-8):充分利用多核的并行收集
  3. 并发时代(JDK 1.5+):追求低延迟的并发收集
  4. 分区时代(JDK 9+):G1的分区化精细管理
  5. 全并发时代(JDK 15+):ZGC的超低延迟全并发

8.2 技术发展趋势

  • 延迟优化:从秒级停顿到毫秒级,再到亚毫秒级
  • 内存扩展:从MB级到GB级,再到TB级支持
  • 并发程度:从串行到并行,再到全并发
  • 智能化:自适应调优和智能参数选择

8.3 选择建议

场景特征 推荐GC 理由
小内存、单核 Serial 简单可靠,资源占用小
多核、高吞吐 Parallel 充分利用CPU,批处理效率高
中等内存、低延迟 G1 平衡性能,停顿可控
大内存、极低延迟 ZGC 支持超大堆,停顿时间极短

8.4 最佳实践

  1. 了解应用特点:内存大小、延迟要求、吞吐量需求
  2. 合理配置参数:根据场景选择合适的JVM参数
  3. 持续监控优化:通过GC日志和监控工具持续调优
  4. 版本升级考虑:新版本JVM通常带来GC性能改进

Java垃圾收集器的演进体现了计算机科学与工程实践的完美结合,通过不断的创新和优化,为不同类型的应用提供了最适合的内存管理解决方案。