文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

Java 深入浅出垃圾回收【一、标记-清除 / 标记-压缩 / 分代收集 / Parallel GC / CMS】

本文将来做一个 源码级别的详细解析,覆盖以下 GC 类型:

  • 标记-清除 (Mark-Sweep)
  • 标记-压缩 (Mark-Compact)
  • 分代 GC (Generational GC)
  • Parallel GC
  • CMS (Concurrent Mark-Sweep)

我会从 算法原理 → HotSpot 源码结构 → 对象流转 & 关键方法 的角度分析。


早期JVM 垃圾回收器演进流程

在这里插入图片描述


1. 标记-清除 (Mark-Sweep)

1.1 算法原理

  1. 标记阶段:遍历堆,标记所有可达对象
  2. 清除阶段:扫描堆,清除未标记对象

优点:实现简单,内存利用率高
缺点:容易产生内存碎片,STW 时间长


1.2 HotSpot 源码结构

    • MarkSweep::collect():主要收集函数
    • mark_live_objects():标记可达对象
    • sweep_dead_objects():清除未标记对象
  • 堆结构

    • OldGenerationTenuredGeneration
    • 链表存储对象(早期 HotSpot 1.2–1.4)
void MarkSweepCollector::collect() {
    mark_live_objects();
    sweep_dead_objects();
}

1.3 对象流转

Heap (OldGen)
 ├─ Object 1 (reachable)
 ├─ Object 2 (unreachable)
 └─ Object 3 (reachable)

标记阶段: Object 1,3 标记
清除阶段: Object 2 删除

2. 标记-压缩 (Mark-Compact)

2.1 算法原理

  1. 标记阶段:标记可达对象
  2. 压缩阶段:将存活对象移动到堆的一端,消除碎片

优点:消除了内存碎片
缺点:移动对象需要额外复制开销


2.2 HotSpot 源码结构

    • MarkCompactCollector:核心类
    • compact():执行压缩搬迁
  • 关键方法

    • sweep_and_compact():扫描并移动对象
    • update_references():更新指针到新地址
void MarkCompactCollector::collect() {
    mark_live_objects();
    sweep_and_compact();
    update_references();
}

3. 分代 GC (Generational GC)

3.1 算法原理

  • 对象“朝生暮死”假设:多数对象生命周期短
  • 年轻代频繁 GC(Minor GC)
  • 老年代偶尔 GC(Major/Full GC)

内存结构

Heap
 ├─ YoungGen
 │   ├─ Eden
 │   └─ Survivor 0/1
 └─ OldGen

3.2 HotSpot 源码结构

  • GenerationalHeap:堆管理入口
  • DefNewGeneration:年轻代
  • TenuredGeneration:老年代

Minor GC 流程

void DefNewGeneration::minor_collection() {
    copy_live_objects(Eden, Survivor);
    promote_to_old_gen();
}

4. Parallel GC

4.1 算法原理

  • 并行复制收集器(年轻代) + 并行标记整理(老年代)
  • STW 期间多线程执行 GC,提高吞吐量

JVM 参数

-XX:+UseParallelGC
-XX:ParallelGCThreads=8

4.2 HotSpot 源码结构

    • PSYoungGen:Parallel Scavenge 年轻代
    • ParallelOld:老年代收集器
    • PSCollector:核心执行类
  • 关键方法

    • do_par_collection():多线程复制存活对象
    • parallel_mark_sweep():老年代多线程标记整理
void PSYoungGen::collect() {
    parallel_copy_live_objects();
    update_survivors();
    promote_objects();
}

5. CMS (Concurrent Mark-Sweep)

5.1 算法原理

  • 目标:减少 STW,低延迟

  • 阶段

    1. 初始标记 (STW)
    2. 并发标记 (并行)
    3. 重新标记 (STW)
    4. 并发清理

优点:STW 时间短,适合延迟敏感应用
缺点:内存碎片多,Full GC 仍需 STW


5.2 HotSpot 源码结构

    • ConcurrentMarkSweepGeneration:老年代管理
    • ParNewGeneration:年轻代(并行复制)
    • CMSCollector:核心收集器
  • 关键方法

    • initial_mark():标记根对象
    • concurrent_mark():并发遍历存活对象
    • remark():STW 修正
    • concurrent_sweep():清理垃圾
void CMSCollector::collect() {
    initial_mark();   // STW
    concurrent_mark(); // 并发
    remark();          // STW
    concurrent_sweep(); // 并发
}

6. 对象流转总结

GC 类型年轻代对象老年代对象STW并发内存碎片
Mark-Sweep标记/清除
Mark-Compact标记/压缩中高
GenerationalMinor GC晋升/Full GC少量
Parallel GC多线程复制多线程标记整理部分
CMSMinor GC并发标记清理

7. 学习源码关键点

  1. Heap 分代结构DefNewGeneration / TenuredGeneration / PSYoungGen / ParallelOld / ConcurrentMarkSweepGeneration
  2. 复制 & 晋升copy_live_objects() / promote_to_old_gen()
  3. 标记 & 清理mark_live_objects() / sweep_dead_objects() / sweep_and_compact()
  4. 并发控制CMSCollector 的初始标记、并发标记、重新标记、并发清理
  5. 调优参数-XX:+UseParallelGC / -XX:ParallelGCThreads / -XX:+UseConcMarkSweepGC

源码实现细节

1. 标记-清除(Mark-Sweep)

原理

  • 标记(Mark):从 GC Roots 出发,通过可达性分析(Reference Chains)标记所有存活对象。
  • 清除(Sweep):遍历堆内存,清除未标记的对象。

JVM 实现

  • HotSpot 中 markSweepPhase 会遍历 GC Roots,并调用 markObject 递归标记。
  • 清除阶段由 SweepClosure 遍历堆,调用 free 释放未标记对象。

源码关键点

文件:hotspot/src/share/vm/gc/serial/markSweep.cpp

void MarkSweep::mark_sweep_phase1() {
    // Root tracing
    mark_from_roots();
}
void MarkSweep::mark_sweep_phase2() {
    // 遍历对象,更新引用
    adjust_pointers();
}
void MarkSweep::mark_sweep_phase3() {
    // 清除未标记对象
    sweep();
}

优缺点

  • ✅ 实现简单
  • ❌ 内存碎片严重,分配新对象可能失败(找不到连续空间)

2. 标记-压缩(Mark-Compact)

原理

  • 标记阶段与 Mark-Sweep 一样。
  • 压缩(Compact):将存活对象向一端移动,清理中间空隙,避免内存碎片。

JVM 实现

  • HotSpot 的 markCompact 算法在 CompactibleSpace 中实现。
  • 通过 forwarding pointer(转发指针)移动对象。

源码关键点

文件:hotspot/src/share/vm/gc/serial/markCompact.cpp

void CompactibleSpace::compact() {
    // 遍历空间,计算对象的新位置
    scan_and_forward();
    // 拷贝对象到新位置
    compact_objects();
}

优缺点

  • ✅ 避免碎片,支持大对象分配
  • ❌ 压缩过程需要 STW,性能较差

3. 分代 GC(Generational GC)

原理

  • 新生代(Young):对象生命周期短,采用复制算法(Copying)。
  • 老年代(Old):对象生命周期长,采用标记-清除或标记-压缩。
  • 新生代分为 Eden + Survivor (From, To)。

JVM 实现

  • HotSpot 中 DefNewGeneration 实现年轻代复制回收。
  • 老年代由 TenuredGeneration 管理。

源码关键点

文件:hotspot/src/share/vm/gc/serial/defNewGeneration.cpp

void DefNewGeneration::collect() {
    evacuate_followers(); // 复制存活对象
    swap_spaces();        // 交换 Survivor 区
}

优缺点

  • ✅ 减少 GC 停顿(大部分对象在年轻代回收)
  • ❌ 老年代 GC 仍然耗时

4. Parallel GC(并行 GC)

原理

  • 新生代和老年代的 GC 都可以并行执行,充分利用多核 CPU。
  • 采用 复制算法 + 标记-压缩算法

JVM 实现

  • ParallelScavengeHeap 管理年轻代,ParallelOldGC 管理老年代。
  • 线程池由 WorkGang 管理,多个 GC Worker 并行标记和清理。

源码关键点

文件:hotspot/src/share/vm/gc/parallel/psParallelCompact.cpp

void PSParallelCompact::invoke() {
    marking_phase(workers);    // 并行标记
    summary_phase(workers);    // 计算转发表
    compaction_phase(workers); // 并行压缩
}

优缺点

  • ✅ 吞吐量高,适合批处理
  • ❌ STW 停顿长,不适合低延迟场景

5. CMS(Concurrent Mark-Sweep)

原理

  • 初始标记(Initial Mark):STW,标记 GC Roots。
  • 并发标记(Concurrent Mark):与应用线程并发运行。
  • 重新标记(Remark):STW,处理并发期间遗漏的对象。
  • 并发清除(Concurrent Sweep):清理垃圾对象。

JVM 实现

  • CMS 使用 写屏障 + 卡表(Card Table)追踪并发期间新对象的引用。
  • CMSCollector 负责调度各阶段。

源码关键点

文件:hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp

void CMSCollector::collect() {
    initial_mark();     // STW
    concurrent_mark();  // 并发
    remark();           // STW
    sweep();            // 并发
}

优缺点

  • ✅ STW 时间短,低延迟
  • ❌ 内存碎片化(使用 Mark-Sweep 而非压缩)
  • ❌ 浮动垃圾(Floating Garbage):并发清理时产生的新垃圾只能留到下次清理

总结对比

GC 类型算法是否并发是否压缩适用场景
Mark-Sweep标记-清除早期 GC
Mark-Compact标记-压缩解决碎片
分代 GCCopying + MC/MS新生代否,老年代可压缩普通应用
Parallel GC并行复制 + 压缩吞吐优先
CMS标记-清除(并发)低延迟场景
posted @ 2025-09-02 10:10  NeoLshu  阅读(4)  评论(0)    收藏  举报  来源