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 算法原理
- 标记阶段:遍历堆,标记所有可达对象
- 清除阶段:扫描堆,清除未标记对象
优点:实现简单,内存利用率高
缺点:容易产生内存碎片,STW 时间长
1.2 HotSpot 源码结构
-
类:
MarkSweep::collect():主要收集函数mark_live_objects():标记可达对象sweep_dead_objects():清除未标记对象
-
堆结构:
OldGeneration或TenuredGeneration- 链表存储对象(早期 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 算法原理
- 标记阶段:标记可达对象
- 压缩阶段:将存活对象移动到堆的一端,消除碎片
优点:消除了内存碎片
缺点:移动对象需要额外复制开销
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,低延迟
-
阶段:
- 初始标记 (STW)
- 并发标记 (并行)
- 重新标记 (STW)
- 并发清理
优点: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 | 无 | 标记/压缩 | 中高 | 否 | 低 |
| Generational | Minor GC | 晋升/Full GC | 少量 | 否 | 中 |
| Parallel GC | 多线程复制 | 多线程标记整理 | 中 | 部分 | 中 |
| CMS | Minor GC | 并发标记清理 | 短 | 是 | 高 |
7. 学习源码关键点
- Heap 分代结构:
DefNewGeneration/TenuredGeneration/PSYoungGen/ParallelOld/ConcurrentMarkSweepGeneration - 复制 & 晋升:
copy_live_objects()/promote_to_old_gen() - 标记 & 清理:
mark_live_objects()/sweep_dead_objects()/sweep_and_compact() - 并发控制:
CMSCollector的初始标记、并发标记、重新标记、并发清理 - 调优参数:
-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 | 标记-压缩 | 否 | 是 | 解决碎片 |
| 分代 GC | Copying + MC/MS | 否 | 新生代否,老年代可压缩 | 普通应用 |
| Parallel GC | 并行复制 + 压缩 | 否 | 是 | 吞吐优先 |
| CMS | 标记-清除(并发) | 是 | 否 | 低延迟场景 |
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120833

浙公网安备 33010602011771号