垃圾回收算法与收集器对比

垃圾回收算法与收集器对比

垃圾回收(GC)是 JVM 自动管理内存的核心机制。理解各种 GC 算法和收集器,是 JVM 调优的基础。

判断对象是否存活

引用计数法

对象A (ref_count=2) <-- 对象B引用
<-- 对象C引用

缺点:无法解决循环引用问题。

可达性分析(JVM采用)

GC Roots
├── 虚拟机栈中的本地变量
├── 方法区中静态属性引用的对象
├── 方法区中常量引用的对象
├── 本地方法栈中JNI引用的对象
└── 所有被同步锁持有的对象

从GC Roots开始向下搜索,不可达的对象可被回收

垃圾回收算法

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

阶段1:标记所有存活对象
┌─────────────────┐
│ ██ ░░ ██ ░░ │ ██=存活 ░░=垃圾
└─────────────────┘

阶段2:清除垃圾对象
┌─────────────────┐
│ ██ ██ │ 产生内存碎片
└─────────────────┘

优点:简单
缺点:产生内存碎片,分配大对象时可能失败

2. 复制(Copying)

阶段1:将存活对象复制到另一块内存
From空间 To空间
┌──────────┐ ┌──────────┐
│ ██ ░░ │ --> │ ██ ██ │
│ ██ ░░ │ │ │
└──────────┘ └──────────┘

阶段2:清空From空间,交换角色

优点:无碎片,效率高
缺点:内存利用率只有 50%

3. 标记-整理(Mark-Compact)

阶段1:标记存活对象
┌─────────────────┐
│ ██ ░░ ██ ░░ │
└─────────────────┘

阶段2:将存活对象向一端移动
┌─────────────────┐
│ ██ ██ │ 无碎片
└─────────────────┘

优点:无碎片,内存利用率高
缺点:移动对象需要更新引用,停顿时间较长

垃圾收集器

1. Serial 收集器

单线程:
┌────────┬────────┬────────┐
│ 工作 │ GC │ 工作 │
└────────┴────────┴────────┘
Stop The World
  • 新生代:复制算法
  • 老年代:Serial Old(标记-整理)
  • 特点:单线程,简单高效
  • 适用:客户端模式、内存小的场景
-XX:+UseSerialGC

2. ParNew 收集器

Serial 的多线程版本,唯一能与 CMS 配合的新生代收集器。

-XX:+UseParNewGC
-XX:ParallelGCThreads=4

3. Parallel Scavenge 收集器

目标:达到可控的吞吐量。

吞吐量 = 运行用户代码时间 / (运行用户代码时间 + GC时间)
-XX:+UseParallelGC           # 使用Parallel Scavenge + Parallel Old
-XX:MaxGCPauseMillis=200 # 最大GC停顿时间
-XX:GCTimeRatio=99 # 吞吐量 = 99 / (1 + 99) = 99%
-XX:+UseAdaptiveSizePolicy # 自适应调节策略

适用:后台计算型任务。

4. CMS 收集器(Concurrent Mark Sweep)

目标:最短停顿时间。

阶段1:初始标记(STW,很快)
阶段2:并发标记(与用户线程并发)
阶段3:重新标记(STW,比初始标记长)
阶段4:并发清除(与用户线程并发)

┌────┬──────────────────┬────┬──────────────────┐
│STW │ 并发标记 │STW│ 并发清除 │
└────┴──────────────────┴────┴──────────────────┘

优点:低停顿
缺点

  • 内存碎片(Mark-Sweep)
  • 对 CPU 资源敏感
  • 无法处理浮动垃圾(Concurrent Mode Failure)
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70 # 老年代70%时触发CMS
-XX:+UseCMSCompactAtFullCollection # Full GC时压缩

5. G1 收集器(Garbage First)

JDK 9 后的默认收集器。

设计思想

┌────┬────┬────┬────┬────┬────┬────┬────┐
│ E │ S │ O │ H │ O │ E │ S │ O │ Region(1~32MB)
└────┴────┴────┴────┴────┴────┴────┴────┘

E=Eden, S=Survivor, O=Old, H=Humongous(大对象)
  • 将堆划分为多个 Region
  • 优先回收垃圾最多的 Region(Garbage First)

回收过程

Young GC:
┌────┬────┬────┬────┬────┬────┬────┬────┐
│ E │ E │ E │ │ │ │ │ │ 回收Eden Region
└────┴────┴────┴────┴────┴────┴────┴────┘

┌────┬────┬────┬────┬────┬────┬────┬────┐
│ S │ │ │ O │ │ │ │ │ 存活对象->Survivor/Old
└────┴────┴────┴────┴────┴────┴────┴────┘

Mixed GC(并发标记后):
回收年轻代 + 部分老年代Region

参数

-XX:+UseG1GC
-XX:MaxGCPauseMillis=200 # 目标最大停顿时间
-XX:G1HeapRegionSize=16m # Region大小
-XX:InitiatingHeapOccupancyPercent=45 # 堆占用45%时触发并发标记

6. ZGC / Shenandoah(低延迟)

JDK 11+(ZGC)和 JDK 12+(Shenandoah)。

目标:TB 级堆内存下停顿时间 < 10ms。

-XX:+UseZGC
-XX:+UseShenandoahGC

收集器对比

收集器 算法 目标 适用场景
Serial 复制/整理 简单高效 单核、小内存
ParNew 复制 降低停顿 配合CMS
Parallel Scavenge 复制/整理 高吞吐量 后台计算
CMS 标记-清除 低停顿 Web应用
G1 复制/整理 平衡 大堆内存
ZGC 复制 超低停顿 大堆、低延迟

组合关系

新生代              老年代
─────────────────────────────
Serial + Serial Old
ParNew + CMS
Parallel Scavenge + Parallel Old
G1(同时管理新生代和老年代)
ZGC(同时管理)

选择建议

场景 推荐
单核/小内存(<100MB) Serial
多核、追求吞吐量 Parallel Scavenge
Web应用、追求低延迟 CMS / G1
大堆内存(>6G) G1
超大堆、极低延迟要求 ZGC
JDK 9+ G1(默认)

总结

算法 优点 缺点
标记-清除 简单 碎片
复制 高效无碎片 空间减半
标记-整理 无碎片 移动对象开销

没有最好的收集器,只有最合适的收集器。根据应用特点(延迟敏感 vs 吞吐量优先)和 JDK 版本选择合适的收集器。


   转载规则


《垃圾回收算法与收集器对比》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录