垃圾收集算法
- 分代收集理论
- 将堆内存划分为年轻代(Young Generation)和老年代(Old Generation)。
- 年轻代使用复制算法(Copying),老年代使用标记清除(Mark-Sweep)或标记整理(Mark-Compact)。
- 复制算法
- 内存对半分,存活对象复制到保留区,清空原区域。
- 优点:高效处理朝生夕死对象。
- 缺点:空间利用率低。
- 标记清除算法
- 标记存活对象后清除未标记对象。
- 缺点:
- 效率问题:大量对象需标记。
- 空间碎片化:导致无法分配大对象。
- 标记整理算法
- 在标记清除基础上,将存活对象向一端移动,避免碎片化。
- 适用于老年代。
垃圾收集器概述
- 垃圾收集器种类
- 常见收集器包括 Serial、Parallel Scavenge、ParNew、CMS、G1、ZGC 等。
- CMS 是面试重点;ZGC 和 G1 是未来趋势。
- 垃圾收集器与算法关系
- 收集器是具体实现,垃圾收集算法是理论基础。
- 不同收集器组合不同算法,例如 CMS 使用标记清除,G1 使用分区+标记整理/复制。
Serial 收集器
- 特点
- 单线程执行 GC。
- STW(Stop-The-World)期间所有用户线程暂停。
- 适用场景
- 单核 CPU 或小内存环境(几十 MB 到几百 MB)。
- 优缺点
- 优点:简单、易于理解。
- 缺点:效率低,资源浪费严重。
Parallel Scavenge 收集器 28:32
- 特点
- 多线程执行 GC。
- 通过
-XX:ParallelGCThreads配置并行线程数,默认与 CPU 核心数一致。
- 适用场景
- 多核 CPU,中等及以上内存。
- 目标
- 提高吞吐量(Throughput)。
ParNew 收集器
- 特点
- 多线程版本的 Serial 收集器。
- 可与 CMS 配合使用。
- 适用场景
- 与 CMS 组合用于 JDK1.8 及之前版本。
CMS 收集器
- 全称
- Concurrent Mark Sweep
- 特点
- 并发执行 GC,减少 STW 时间。
- 使用标记清除算法。
- 工作流程
- 初始标记:STW,标记 GC Roots 直接关联对象。
- 并发标记:与用户线程并发执行,标记整个堆中存活对象。
- 重新标记:STW,修正并发标记期间变化的对象。
- 并发清除:清除未标记对象。
- 并发重置:清理标记信息,为下一轮 GC 做准备。
- 优点
- 用户感知停顿时间短,适合大内存应用。
- 缺点
- 浮动垃圾:并发阶段产生的新垃圾只能在下次 GC 清除。
- 内存碎片:使用标记清除算法,易产生碎片。
- Concurrent Mode Failure:并发失败时触发 Full GC,切换至 Serial Old,性能骤降。
CMS 核心参数
- 启用 CMS
-XX:+UseConcMarkSweepGC
- 设置并发线程数
-XX:ConcGCThreads=...
- 启用压缩整理
-XX:+UseCMSCompactAtFullCollection
- 压缩周期
-XX:CMSFullGCsBeforeCompaction=...
- 老年代触发 GC 比例
-XX:CMSInitiatingOccupancyFraction=...
CMS 与 Parallel 对比
- Parallel 特点
- 更关注吞吐量。
- 所有 GC 阶段都 STW,整体耗时更短但用户体验差。
- CMS 特点
- 更关注用户体验。
- 虽总耗时长,但大部分阶段并发执行,STW 时间分散且短暂。
三色标记算法
- 三种颜色定义
- 黑色:该对象引用的所有对象已扫描完毕。
- 灰色:该对象被扫描,但部分引用未扫描。
- 白色:未被扫描或不可达对象。
- 漏标问题
- 场景:并发标记过程中对象引用关系发生变化。
- 示例:A 引用 B,B 引用 D → B 引用断开,A 新增引用 D。
- 结果:D 被误判为白色对象(即垃圾),可能被错误回收。
- 解决方案
- 增量更新(Incremental Update)
- 记录新增引用,在重新标记阶段重新扫描源头对象。
- 原始快照(Snapshot At The Beginning, SATB)
- 记录删除的引用,在重新标记阶段标记相关对象为黑色。
- 增量更新(Incremental Update)
写屏障(Write Barrier)
- 作用
- 在赋值操作前后插入逻辑,记录对象引用变化。
- 类型
- 写前屏障(Pre-Write Barrier)
- 用于 SATB,记录旧引用。
- 写后屏障(Post-Write Barrier)
- 用于增量更新,记录新引用。
- 写前屏障(Pre-Write Barrier)
- 源码示例
- C++ 层级伪代码模拟写屏障行为。
- 通过队列异步处理记录的引用,提升性能。