Skip to content

G1 GC(垃圾优先垃圾回收器)

概述

G1(Garbage First)GC 是一个面向服务端应用的垃圾回收器,设计目标是在保持高吞吐量的同时实现低延迟。G1 GC 采用分区域的内存管理方式,将堆内存划分为多个大小相等的区域(Region),并通过优先回收垃圾最多的区域来获得最大的回收效益。从 JDK 9 开始,G1 GC 成为默认的垃圾回收器。

基本特征

核心特点

  • 区域化管理:将堆内存划分为多个独立的区域
  • 可预测停顿:可以设置期望的最大停顿时间
  • 并发回收:大部分回收工作与应用线程并发执行
  • 增量回收:每次只回收部分区域,避免长时间停顿
  • 自适应调节:根据历史数据动态调整回收策略

适用场景

  • 大内存应用:堆内存 4GB 以上
  • 延迟敏感服务:要求可预测的停顿时间
  • 高吞吐量应用:需要平衡吞吐量和延迟
  • 现代服务架构:微服务、云原生应用

内存布局

G1 GC 将堆内存划分为多个大小相等的区域(Region):

G1 GC 堆内存布局:
┌─────────────────────────────────────────────────────────────┐
│                        G1 堆内存                            │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │
│ │ E   │ E   │ S   │ O   │ O   │ H   │ E   │ S   │ O   │ O   │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │
│ │ O   │ F   │ F   │ E   │ E   │ S   │ O   │ H   │ F   │ F   │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │
└─────────────────────────────────────────────────────────────┘

区域类型说明:
E = Eden 区域(新生代)
S = Survivor 区域(新生代)
O = Old 区域(老年代)
H = Humongous 区域(大对象)
F = Free 区域(空闲)

区域特点:
- 每个区域大小:1MB - 32MB(通常 1MB-2MB)
- 区域总数:通常 2048 个左右
- 动态分配:区域类型可以动态改变
- 大对象:超过区域大小 50% 的对象存储在 Humongous 区域

区域管理

G1 区域管理详细说明:

1. Eden 区域:
   - 新对象分配的区域
   - 当 Eden 区域满时触发 Minor GC
   - 回收后变为 Free 区域

2. Survivor 区域:
   - 存储从 Eden 区域存活下来的对象
   - 分为 From 和 To 两个逻辑区域
   - 对象在 Survivor 区域间复制

3. Old 区域:
   - 存储长期存活的对象
   - 从 Survivor 区域晋升的对象
   - 大对象可能直接分配到此

4. Humongous 区域:
   - 存储大对象(> 区域大小的 50%)
   - 可能占用连续的多个区域
   - 在 Mixed GC 中回收

5. Free 区域:
   - 空闲区域,可以分配给任何类型
   - 动态分配给 Eden、Survivor 或 Old
   - 提供内存分配的灵活性

工作流程

G1 GC 有多种回收模式,根据堆内存使用情况自动选择:

1. Young GC(新生代回收)

Young GC 详细流程:

1. 触发条件:
   - Eden 区域分配满
   - 新对象无法分配到 Eden 区域
   - 定期检查是否需要回收

2. 回收过程:
   a) 暂停应用线程(STW)
   b) 扫描 GC Roots 和跨区域引用
   c) 复制存活对象到 Survivor 或 Old 区域
   d) 更新对象年龄和引用关系
   e) 清空 Eden 区域,标记为 Free
   f) 恢复应用线程

3. 对象处理:
   - 新分配对象:回收(变为垃圾)
   - 存活对象:复制到 Survivor 区域
   - 老对象:年龄增加,可能晋升到 Old 区域
   - 大对象:直接分配到 Humongous 区域

4. 区域状态变化:
   - Eden 区域 → Free 区域
   - 部分 Free 区域 → Survivor 区域
   - 可能分配新的 Old 区域

2. Concurrent Marking(并发标记)

并发标记详细流程:

1. 触发条件:
   - 堆内存使用率达到阈值(默认 45%)
   - 上次并发标记完成后的时间间隔
   - 预测即将需要 Mixed GC

2. 标记阶段:
   a) 初始标记(Initial Mark)- STW
      - 标记 GC Roots 直接引用的对象
      - 通常与 Young GC 一起执行
      - 停顿时间很短
   
   b) 根区域扫描(Root Region Scan)- 并发
      - 扫描 Survivor 区域中指向老年代的引用
      - 与应用线程并发执行
      - 必须在下次 Young GC 前完成
   
   c) 并发标记(Concurrent Mark)- 并发
      - 遍历整个对象图,标记存活对象
      - 使用三色标记算法
      - 与应用线程并发执行
   
   d) 重新标记(Remark)- STW
      - 处理并发标记期间的引用变化
      - 使用 SATB(Snapshot At The Beginning)算法
      - 完成最终的标记工作
   
   e) 清理(Cleanup)- STW
      - 统计每个区域的存活对象
      - 回收完全空的区域
      - 为 Mixed GC 做准备

3. 标记结果:
   - 确定每个区域的存活对象数量
   - 计算回收效益(垃圾对象数量)
   - 为 Mixed GC 选择回收区域

3. Mixed GC(混合回收)

txt
Mixed GC 详细流程:

1. 触发条件:
   - 并发标记完成
   - 老年代区域中有足够的垃圾
   - 预测回收时间在目标停顿时间内

2. 回收策略:
   - 回收所有 Eden 区域
   - 回收所有 Survivor 区域
   - 回收部分 Old 区域(垃圾最多的)
   - 回收 Humongous 区域(如果需要)

3. 回收过程:
   a) 暂停应用线程(STW)
   b) 选择回收区域集合(Collection Set)
   c) 扫描 GC Roots 和跨区域引用
   d) 并行复制存活对象到新区域
   e) 更新引用关系和记忆集
   f) 清空回收区域,标记为 Free
   g) 恢复应用线程

4. 区域选择算法:
   - 计算每个区域的回收效益
   - 优先选择垃圾最多的区域
   - 考虑停顿时间目标
   - 平衡回收效果和停顿时间

4. Full GC(完整回收)

Full GC 详细流程:

1. 触发条件:
   - 堆内存严重不足
   - Mixed GC 无法释放足够空间
   - 大对象分配失败
   - 元空间不足(JDK 8+)

2. 回收过程:
   - 使用单线程 Serial Old 算法
   - 标记-整理算法
   - 长时间停顿(秒级)
   - 回收整个堆内存

3. 避免策略:
   - 合理设置堆大小
   - 调整并发标记阈值
   - 优化 Mixed GC 参数
   - 监控内存使用情况

算法实现

G1 回收算法

java
// G1 垃圾回收器主要算法实现
public class G1Collector {
    private final RegionManager regionManager;
    private final ConcurrentMarkingThread markingThread;
    private final CollectionSetChooser csChooser;
    private final RememberedSetManager rsManager;
    
    // G1 回收的主要入口
    public void collect(GCCause cause) {
        switch (cause) {
            case ALLOCATION_FAILURE:
                youngGC();
                break;
            case G1_HUMONGOUS_ALLOCATION:
                if (shouldStartConcurrentMark()) {
                    startConcurrentMark();
                }
                youngGC();
                break;
            case G1_PERIODIC_COLLECTION:
                periodicGC();
                break;
            default:
                fullGC();
        }
    }
    
    // Young GC 实现
    private void youngGC() {
        // 1. 暂停应用线程
        stopAllApplicationThreads();
        
        try {
            // 2. 选择回收集合
            CollectionSet cs = selectYoungCollectionSet();
            
            // 3. 扫描根对象
            RootScanner rootScanner = new RootScanner();
            rootScanner.scanRoots(cs);
            
            // 4. 并行复制对象
            ParallelObjectCopier copier = new ParallelObjectCopier();
            copier.copyObjects(cs);
            
            // 5. 更新引用和记忆集
            updateReferencesAndRememberedSets(cs);
            
            // 6. 释放回收区域
            regionManager.freeRegions(cs.getRegions());
            
            // 7. 更新统计信息
            updateGCStatistics();
            
        } finally {
            // 8. 恢复应用线程
            resumeAllApplicationThreads();
        }
    }
    
    // 并发标记实现
    private void startConcurrentMark() {
        markingThread.startMarking();
    }
    
    // Mixed GC 实现
    private void mixedGC() {
        stopAllApplicationThreads();
        
        try {
            // 1. 选择混合回收集合
            CollectionSet cs = selectMixedCollectionSet();
            
            // 2. 扫描根对象和跨区域引用
            scanRootsAndRememberedSets(cs);
            
            // 3. 并行复制存活对象
            parallelCopyLiveObjects(cs);
            
            // 4. 更新引用关系
            updateReferences(cs);
            
            // 5. 清理回收区域
            cleanupCollectedRegions(cs);
            
        } finally {
            resumeAllApplicationThreads();
        }
    }
    
    // 选择回收集合
    private CollectionSet selectMixedCollectionSet() {
        CollectionSet cs = new CollectionSet();
        
        // 添加所有 Eden 区域
        cs.addAll(regionManager.getEdenRegions());
        
        // 添加所有 Survivor 区域
        cs.addAll(regionManager.getSurvivorRegions());
        
        // 选择垃圾最多的 Old 区域
        List<Region> oldRegions = csChooser.chooseOldRegions(
            pauseTimeTarget, 
            regionManager.getOldRegions()
        );
        cs.addAll(oldRegions);
        
        return cs;
    }
}

并发标记算法

java
// G1 并发标记算法实现
public class G1ConcurrentMarking {
    private final MarkBitMap markBitMap;
    private final SATBMarkQueue satbQueue;
    private final RegionManager regionManager;
    
    public void concurrentMark() {
        try {
            // 1. 初始标记(与 Young GC 一起执行)
            initialMark();
            
            // 2. 根区域扫描
            rootRegionScan();
            
            // 3. 并发标记
            concurrentMarkPhase();
            
            // 4. 重新标记
            remark();
            
            // 5. 清理
            cleanup();
            
        } catch (InterruptedException e) {
            // 处理中断
            Thread.currentThread().interrupt();
        }
    }
    
    private void initialMark() {
        // 在 Young GC 的 STW 期间执行
        // 标记 GC Roots 直接引用的对象
        for (GCRoot root : getGCRoots()) {
            Object obj = root.getObject();
            if (obj != null) {
                markBitMap.mark(obj);
                satbQueue.enqueue(obj);
            }
        }
    }
    
    private void rootRegionScan() {
        // 扫描 Survivor 区域中指向老年代的引用
        for (Region region : regionManager.getSurvivorRegions()) {
            scanRegionForOldReferences(region);
        }
    }
    
    private void concurrentMarkPhase() {
        // 并发标记阶段
        while (!satbQueue.isEmpty()) {
            Object obj = satbQueue.dequeue();
            
            if (obj != null && markBitMap.isMarked(obj)) {
                // 扫描对象的引用
                for (Object ref : obj.getReferences()) {
                    if (ref != null && !markBitMap.isMarked(ref)) {
                        if (markBitMap.mark(ref)) {
                            satbQueue.enqueue(ref);
                        }
                    }
                }
            }
            
            // 检查是否需要让出 CPU
            if (shouldYield()) {
                Thread.yield();
            }
        }
    }
    
    private void remark() {
        // STW 阶段,处理并发标记期间的变化
        stopAllApplicationThreads();
        
        try {
            // 处理 SATB 缓冲区
            processSATBBuffers();
            
            // 完成剩余的标记工作
            finishMarking();
            
        } finally {
            resumeAllApplicationThreads();
        }
    }
    
    private void cleanup() {
        stopAllApplicationThreads();
        
        try {
            // 统计每个区域的存活对象
            for (Region region : regionManager.getAllRegions()) {
                int liveObjects = countLiveObjects(region);
                region.setLiveObjectCount(liveObjects);
                
                // 完全空的区域直接回收
                if (liveObjects == 0) {
                    regionManager.freeRegion(region);
                }
            }
            
            // 为 Mixed GC 准备候选区域
            prepareForMixedGC();
            
        } finally {
            resumeAllApplicationThreads();
        }
    }
}

SATB 算法实现

java
// SATB(Snapshot At The Beginning)算法实现
public class SATBAlgorithm {
    private final SATBMarkQueue globalQueue;
    private final ThreadLocal<SATBMarkQueue> localQueues;
    
    // SATB 写屏障
    public void satbWriteBarrier(Object from, Object to, int fieldOffset) {
        // 获取原始引用值
        Object oldValue = from.getReference(fieldOffset);
        
        // 执行原始的引用写入
        from.setReference(fieldOffset, to);
        
        // SATB 处理:记录被覆盖的引用
        if (isConcurrentMarkingActive() && oldValue != null) {
            // 将被覆盖的引用加入 SATB 队列
            SATBMarkQueue localQueue = localQueues.get();
            if (localQueue.isFull()) {
                // 本地队列满,转移到全局队列
                globalQueue.addAll(localQueue.drain());
            }
            localQueue.enqueue(oldValue);
        }
        
        // 跨区域引用处理
        if (from.getRegion() != to.getRegion()) {
            updateRememberedSet(from, to);
        }
    }
    
    // 处理 SATB 队列
    public void processSATBQueues() {
        // 处理全局队列
        while (!globalQueue.isEmpty()) {
            Object obj = globalQueue.dequeue();
            if (obj != null && !markBitMap.isMarked(obj)) {
                markBitMap.mark(obj);
                scanObject(obj);
            }
        }
        
        // 处理所有线程的本地队列
        for (Thread thread : getAllApplicationThreads()) {
            SATBMarkQueue localQueue = localQueues.get();
            while (!localQueue.isEmpty()) {
                Object obj = localQueue.dequeue();
                if (obj != null && !markBitMap.isMarked(obj)) {
                    markBitMap.mark(obj);
                    scanObject(obj);
                }
            }
        }
    }
}

记忆集(Remembered Set)实现

java
// G1 记忆集实现
public class G1RememberedSet {
    private final Map<Region, Set<CardIndex>> rememberedSets;
    private final CardTable cardTable;
    
    // 更新记忆集
    public void updateRememberedSet(Object from, Object to) {
        Region fromRegion = from.getRegion();
        Region toRegion = to.getRegion();
        
        if (fromRegion != toRegion) {
            // 跨区域引用,更新目标区域的记忆集
            CardIndex cardIndex = cardTable.getCardIndex(from.getAddress());
            
            Set<CardIndex> rs = rememberedSets.get(toRegion);
            if (rs == null) {
                rs = new ConcurrentHashMap<>();
                rememberedSets.put(toRegion, rs);
            }
            rs.add(cardIndex);
            
            // 标记卡片为脏
            cardTable.markCardDirty(cardIndex);
        }
    }
    
    // 扫描记忆集
    public void scanRememberedSet(Region region, ObjectVisitor visitor) {
        Set<CardIndex> rs = rememberedSets.get(region);
        if (rs != null) {
            for (CardIndex cardIndex : rs) {
                if (cardTable.isCardDirty(cardIndex)) {
                    // 扫描脏卡片中的对象
                    scanCard(cardIndex, visitor);
                    
                    // 清理卡片
                    cardTable.clearCard(cardIndex);
                }
            }
        }
    }
    
    private void scanCard(CardIndex cardIndex, ObjectVisitor visitor) {
        Address cardStart = cardTable.getCardAddress(cardIndex);
        Address cardEnd = cardStart.plus(CARD_SIZE);
        
        // 遍历卡片中的所有对象
        for (Address addr = cardStart; addr.lessThan(cardEnd); ) {
            Object obj = getObjectAt(addr);
            if (obj != null) {
                visitor.visit(obj);
                addr = addr.plus(obj.getSize());
            } else {
                break;
            }
        }
    }
}

性能特征

优势

  1. 可预测停顿

    • 可以设置最大停顿时间目标
    • 通过增量回收控制停顿时间
    • 适合延迟敏感应用
  2. 高吞吐量

    • 并发回收减少应用线程停顿
    • 并行回收提高回收效率
    • 适合高吞吐量应用
  3. 内存效率

    • 区域化管理减少内存碎片
    • 增量回收避免长时间停顿
    • 自适应调节优化性能
  4. 可扩展性

    • 支持大内存堆(64GB+)
    • 并行度随 CPU 核数扩展
    • 适合现代多核服务器

劣势

  1. 内存开销

    • 记忆集占用额外内存(约 10-20%)
    • 标记位图占用内存
    • 区域管理元数据开销
  2. 复杂性

    • 算法复杂,调优参数多
    • 问题诊断相对困难
    • 需要深入理解原理
  3. 小堆性能

    • 小于 4GB 堆内存性能不如 Parallel GC
    • 固定开销相对较大
    • 不适合内存受限环境

JVM 参数配置

基础参数

bash
# 启用 G1 GC
-XX:+UseG1GC                    # 启用 G1 垃圾回收器

# 堆内存设置
-Xms8g                          # 初始堆大小
-Xmx32g                         # 最大堆大小

# 停顿时间目标
-XX:MaxGCPauseMillis=200        # 最大停顿时间目标(毫秒)

# 区域大小
-XX:G1HeapRegionSize=16m        # 区域大小(1MB-32MB)

G1 核心参数

bash
# 并发标记相关
-XX:G1MixedGCCountTarget=8              # Mixed GC 目标次数
-XX:G1MixedGCLiveThresholdPercent=85    # Mixed GC 存活对象阈值
-XX:G1OldCSetRegionThresholdPercent=10  # 老年代回收区域比例上限

# 并发线程数
-XX:ConcGCThreads=8                     # 并发 GC 线程数
-XX:ParallelGCThreads=16                # 并行 GC 线程数

# 触发条件
-XX:G1HeapWastePercent=5                # 堆浪费百分比
-XX:G1ReservePercent=10                 # 堆保留百分比

内存管理参数

bash
# 大对象处理
-XX:G1HeapRegionSize=32m                # 增大区域大小处理大对象

# 新生代设置
-XX:G1NewSizePercent=20                 # 新生代最小比例
-XX:G1MaxNewSizePercent=40              # 新生代最大比例

# 并发标记阈值
-XX:G1ConcRefinementThreads=8           # 并发优化线程数

高级参数

bash
# 字符串去重
-XX:+UseStringDeduplication             # 启用字符串去重

# 类卸载
-XX:+ClassUnloadingWithConcurrentMark   # 并发标记时卸载类

# 调试参数
-XX:+PrintGCDetails                     # 打印详细 GC 信息
-XX:+PrintG1PrintRegionRememberedSetInfo # 打印记忆集信息
-XX:+TraceClassUnloading                # 跟踪类卸载

监控与调优

关键指标

  1. 停顿时间指标

    • 平均停顿时间
    • 最大停顿时间
    • 停顿时间分布
  2. 吞吐量指标

    • GC 时间占比
    • 应用运行时间占比
    • 每秒处理请求数
  3. 内存指标

    • 堆内存使用率
    • 区域分配情况
    • Mixed GC 频率

调优策略

停顿时间调优

bash
# 减少停顿时间
-XX:MaxGCPauseMillis=100        # 降低停顿时间目标
-XX:G1MixedGCCountTarget=16     # 增加 Mixed GC 次数
-XX:G1OldCSetRegionThresholdPercent=5  # 减少单次回收区域

# 增加并行度
-XX:ParallelGCThreads=20        # 增加并行线程数

吞吐量调优

bash
# 提高吞吐量
-XX:MaxGCPauseMillis=500        # 允许更长停顿时间
-XX:G1MixedGCCountTarget=4      # 减少 Mixed GC 次数
-XX:G1OldCSetRegionThresholdPercent=20  # 增加单次回收区域

# 减少 GC 频率
-XX:G1HeapWastePercent=10       # 允许更多堆浪费

内存调优

bash
# 大对象优化
-XX:G1HeapRegionSize=32m        # 增大区域大小

# 新生代优化
-XX:G1NewSizePercent=30         # 增加新生代比例
-XX:G1MaxNewSizePercent=50      # 允许更大新生代

# 并发标记优化
-XX:ConcGCThreads=12            # 增加并发线程数

监控工具

JVM 内置工具

bash
# 查看 G1 统计
jstat -gc <pid> 1s

# 查看 G1 详细信息
jstat -gccapacity <pid>

# 查看区域信息
jcmd <pid> GC.run_finalization

# 查看字符串去重统计
jcmd <pid> VM.stringdedup

GC 日志分析

bash
# 启用详细 G1 日志
-XX:+PrintGCDetails \
-XX:+PrintGCTimeStamps \
-XX:+PrintGCApplicationStoppedTime \
-XX:+PrintG1PrintRegionRememberedSetInfo \
-Xloggc:g1-gc.log

# 关注的日志信息:
# - [GC pause (young)]: Young GC 信息
# - [GC pause (mixed)]: Mixed GC 信息
# - [GC concurrent-mark-start]: 并发标记开始
# - [GC concurrent-mark-end]: 并发标记结束

典型应用场景

1. 大内存 Web 应用

bash
# 大内存 Web 服务配置
-XX:+UseG1GC
-Xms16g -Xmx64g
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=32m
-XX:G1NewSizePercent=25
-XX:G1MaxNewSizePercent=40
-XX:ParallelGCThreads=16
-XX:ConcGCThreads=8

2. 微服务架构

bash
# 微服务应用配置
-XX:+UseG1GC
-Xms8g -Xmx32g
-XX:MaxGCPauseMillis=100
-XX:G1HeapRegionSize=16m
-XX:G1MixedGCCountTarget=12
-XX:+UseStringDeduplication
-XX:+ClassUnloadingWithConcurrentMark

3. 数据处理服务

bash
# 数据处理服务配置
-XX:+UseG1GC
-Xms32g -Xmx128g
-XX:MaxGCPauseMillis=500
-XX:G1HeapRegionSize=32m
-XX:G1NewSizePercent=30
-XX:G1HeapWastePercent=10
-XX:ParallelGCThreads=32

与其他 GC 对比

特性G1 GCCMS GCParallel GCZGC
停顿时间可控极短
吞吐量中等最高中等
内存开销中高中等
内存碎片
适用堆大小4GB-64GB4GB-32GB< 32GB> 64GB
并发程度部分
可预测性中等
复杂度中等简单

最佳实践

1. 选择标准

  • 堆内存大小:4GB 以上推荐使用 G1
  • 延迟要求:需要可预测的停顿时间
  • 吞吐量要求:需要平衡吞吐量和延迟
  • 应用特性:现代服务端应用

2. 配置建议

  • 合理设置停顿时间目标:不要设置过于激进的目标
  • 适当调整区域大小:根据对象大小分布调整
  • 监控 Mixed GC 频率:避免过于频繁的 Mixed GC
  • 启用有用的特性:字符串去重、类卸载等

3. 注意事项

  • 避免频繁 Full GC:监控并调整参数
  • 关注内存开销:记忆集和元数据开销
  • 定期性能测试:验证调优效果
  • 监控应用指标:不只关注 GC 指标

4. 迁移建议

  • 从 CMS 迁移:当需要更好的可预测性时
  • 从 Parallel 迁移:当延迟成为瓶颈时
  • 迁移到 ZGC:当需要极低延迟时

故障排查

常见问题

  1. 停顿时间超过目标

    原因:目标设置过于激进或堆内存不足
    解决:
    - 调整停顿时间目标
    - 增加堆内存
    - 优化应用代码
  2. Mixed GC 过于频繁

    原因:老年代增长过快或阈值设置不当
    解决:
    - 调整并发标记阈值
    - 增加新生代大小
    - 优化对象生命周期
  3. Full GC 频繁发生

    原因:堆内存不足或参数配置不当
    解决:
    - 增加堆内存
    - 调整 G1 参数
    - 检查内存泄漏

诊断工具

bash
# 分析 GC 日志
-XX:+PrintGCDetails \
-XX:+PrintGCCause \
-XX:+PrintStringDeduplicationStatistics

# 监控内存使用
jstat -gc <pid> 1s

# 分析堆转储
jmap -dump:format=b,file=heap.hprof <pid>

# 查看 G1 内部状态
jcmd <pid> GC.run_finalization

发展历程与未来

历史发展

  • JDK 7u4:首次引入 G1 GC(实验性)
  • JDK 8:G1 GC 稳定版本
  • JDK 9:G1 GC 成为默认垃圾回收器
  • JDK 10+:持续优化和改进

未来发展

  • 性能优化:继续优化停顿时间和吞吐量
  • 内存效率:减少内存开销
  • 易用性:简化配置和调优
  • 新特性:支持更多高级特性

总结

G1 GC 是一个设计优秀的现代垃圾回收器,它成功地平衡了停顿时间和吞吐量,特别适合大内存、延迟敏感的服务端应用。通过区域化的内存管理和增量回收策略,G1 GC 提供了可预测的停顿时间,同时保持了较高的吞吐量。虽然它有一定的内存开销和复杂性,但对于现代应用来说,这些代价是值得的。随着 JDK 的不断发展,G1 GC 将继续优化,为 Java 应用提供更好的垃圾回收体验。