jvm垃圾回收

4 阅读6分钟

JVM 垃圾回收器及其原理与垃圾回收算法

垃圾回收(Garbage Collection, GC)是 JVM 自动管理内存的核心机制。GC 的目标是识别并回收不再使用的对象,释放内存空间。JVM 提供了多种垃圾回收器,每种回收器基于不同的算法,适用于不同的业务场景(吞吐量优先、低延迟优先等)。


一、垃圾回收算法

所有垃圾回收器都基于以下三种基础算法或其组合:

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

  • 过程

    1. 标记:从 GC Roots 出发,遍历所有可达对象,进行标记。
    2. 清除:遍历堆,回收未被标记的对象内存。
  • 优点:实现简单,不需要移动对象。

  • 缺点:产生内存碎片,导致后续无法为大对象分配连续空间;效率随对象数量增多而下降。

  • 代表:CMS 的“并发清除”阶段。

2. 标记-复制(Mark-Copy)

  • 过程:将内存分为两块(如 Eden 和 Survivor),每次只使用一块。当该块满时,将存活对象复制到另一块,然后一次性清除整块。
  • 优点:实现简单,无碎片;复制过程高效。
  • 缺点:内存利用率低,始终有一块空间空闲。
  • 代表:新生代的所有回收器(Serial、ParNew、Parallel Scavenge)。

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

  • 过程

    1. 标记存活对象(同标记-清除)。
    2. 将所有存活对象向一端移动,整理出连续的空闲空间。
  • 优点:无内存碎片。

  • 缺点:移动对象需要更新引用,停顿时间较长。

  • 代表:老年代回收器(Serial Old、Parallel Old、CMS 的 Full GC 阶段)。

4. 分代收集理论

现代 JVM 将堆分为新生代(Young Generation)和老年代(Old Generation),并采用不同的回收算法:

  • 新生代:对象“朝生夕死”,适合标记-复制(效率高,碎片少)。
  • 老年代:对象存活率高,适合标记-清除标记-整理(减少移动成本)。

二、垃圾回收器分类与原理

JVM 垃圾回收器按工作区域分为新生代回收器老年代回收器统合型回收器(整堆回收)。

回收器工作区域算法特点适用场景
Serial新生代标记-复制(单线程)单线程,Stop-The-World,简单高效客户端模式,单 CPU,小堆内存
Serial Old老年代标记-整理(单线程)同上同上,或作为 CMS 的备用 Full GC
ParNew新生代标记-复制(多线程)Serial 的多线程版本,常与 CMS 配合多 CPU 环境,与 CMS 搭配使用
Parallel Scavenge新生代标记-复制(多线程)吞吐量优先,可控制吞吐量和最大停顿后台计算,高吞吐量场景
Parallel Old老年代标记-整理(多线程)吞吐量优先,与 Parallel Scavenge 搭配同上
CMS老年代标记-清除(并发)低延迟,并发回收,但产生碎片互联网应用,对响应时间敏感
G1整堆分区 + 标记-复制/整理可预测停顿,Region 化,并发大堆内存(4GB+),平衡吞吐与延迟
ZGC整堆并发,基于读屏障超低延迟(<10ms),可扩展至 TB 级堆超大堆,极低延迟需求(JDK 11+)
Shenandoah整堆并发,基于读屏障与 ZGC 类似,低延迟OpenJDK 12+,低延迟场景

三、主流垃圾回收器详解

1. Serial 与 Serial Old

  • Serial:新生代单线程,使用 标记-复制。GC 时暂停所有应用线程(Stop-The-World),适合单 CPU 或小内存环境。
  • Serial Old:老年代单线程,使用 标记-整理。作为 CMS 的备选 Full GC 方案。

2. Parallel Scavenge 与 Parallel Old

  • Parallel Scavenge:新生代多线程,标记-复制。关注吞吐量(-XX:GCTimeRatio)和最大停顿时间(-XX:MaxGCPauseMillis)。
  • Parallel Old:老年代多线程,标记-整理。与 Parallel Scavenge 搭配,实现高吞吐量回收。

3. CMS(Concurrent Mark Sweep)

  • 目标:最小化暂停时间。

  • 步骤

    1. 初始标记(STW):标记 GC Roots 直接关联的对象。
    2. 并发标记:与用户线程并发,从 GC Roots 遍历对象图。
    3. 重新标记(STW):修正并发标记期间发生变化的对象。
    4. 并发清除:清除未被标记的对象,产生碎片。
  • 缺点:CPU 敏感;产生内存碎片;并发模式失败(Concurrent Mode Failure)时会退化为 Serial Old Full GC。

4. G1(Garbage First)

  • 目标:可预测的停顿时间(-XX:MaxGCPauseMillis)。

  • 设计:将堆划分为多个大小相等的 Region(1MB~32MB),每个 Region 可扮演 Eden、Survivor、Old 或 Humongous(巨型对象)角色。

  • 工作流程

    1. 初始标记(STW):标记 GC Roots。
    2. 并发标记:并发标记整个堆。
    3. 最终标记(STW):处理 SATB(Snapshot-At-The-Beginning)缓冲区。
    4. 筛选回收(STW):评估各 Region 的回收价值和成本,选择收益高的 Region 回收(使用标记-复制)。
  • 特点:通过 Region 和停顿预测模型,实现可控制的停顿时间;避免全堆扫描。

5. ZGC

  • 目标:亚毫秒级停顿(<10ms),支持 TB 级堆。
  • 设计:基于 染色指针(Colored Pointers)  和 读屏障。GC 阶段几乎全部并发,只有短暂(<1ms)的 STW 阶段。
  • 工作流程:并发标记、并发整理、并发重映射等,利用内存多重映射技术实现对象移动时不暂停应用。
  • JDK 版本:JDK 11 实验性,JDK 15 正式可用。

6. Shenandoah

  • 目标:与 ZGC 类似,低延迟。
  • 设计:基于 转发指针(Brooks Pointer)  和 并发整理,通过读屏障处理对象移动后的引用更新。
  • JDK 版本:OpenJDK 12 起正式支持。

四、回收器选择与调优建议

  1. 客户端模式 / 单 CPU / 小堆(<512MB) :Serial(-XX:+UseSerialGC)。

  2. 高吞吐量(后台计算、批处理) :Parallel Scavenge + Parallel Old(-XX:+UseParallelGC 或 -XX:+UseParallelOldGC)。

  3. 低延迟(Web 服务、中间件)

    • 堆内存 < 4GB:CMS(-XX:+UseConcMarkSweepGC),但注意碎片和并发模式失败。
    • 堆内存 4GB ~ 32GB:G1(-XX:+UseG1GC),设置 -XX:MaxGCPauseMillis=100~200
    • 堆内存 > 32GB 且要求极低延迟:ZGC(-XX:+UseZGC)或 Shenandoah(-XX:+UseShenandoahGC)。
  4. 混合场景:G1 是 JDK 9+ 的默认回收器,平衡了吞吐量和延迟。


五、总结

算法优点缺点代表回收器
标记-清除实现简单,不移动对象内存碎片CMS(并发清除)
标记-复制高效,无碎片内存利用率低所有新生代回收器
标记-整理无碎片移动对象开销大Serial Old,Parallel Old

垃圾回收器的选择需结合业务对吞吐量延迟的要求。现代应用多倾向于使用 G1 或 ZGC 这类可控停顿的回收器,同时配合 GC 日志分析,不断调优参数以达到最佳性能。