Java垃圾收集器全解:从Serial到G1的进化之旅

----------------- 先赞后看 👍 效果翻倍 🔥 -----------------

概述

在Java的世界里,垃圾收集器就像是默默无闻的清洁工,在我们不注意的时候悄悄清理内存垃圾。不同的清洁工有不同的工作方式,有的喜欢一次性彻底打扫(Stop The World),有的则喜欢边工作边让你继续玩耍(并发收集)。今天,就让我们一起来认识这些各具特色的"清洁工"吧!

收集器家族一览

收集器 工作方式 适用分代 特点 适用场景
Serial 单线程 新生代 简单高效,停顿时间长 客户端应用,小内存
ParNew 多线程 新生代 Serial的多线程版本 与CMS配合
Parallel Scavenge 多线程 新生代 吞吐量优先 后台运算,不注重交互
Serial Old 单线程 老年代 Serial的老年代版本 客户端,CMS备胎
Parallel Old 多线程 老年代 Parallel Scavenge的老年代版 吞吐量优先应用
CMS 并发 老年代 低停顿时间 B/S系统,重视响应
G1 并发+并行 全堆 可控停顿时间 大内存服务端应用

新生代收集器

1. Serial收集器 - "单干的老黄牛"

工作方式 :单线程,Stop The World
算法 :标记-复制
适用场景:客户端模式,内存受限环境

Serial收集器就像是一位勤劳的清洁阿姨,每次打扫时都需要你暂时离开房间(Stop The World)。她工作认真,但没有帮手,只能一件一件地清理。

详细介绍

Serial收集器是Java最古老的新生代收集器,采用单线程执行垃圾回收工作。当它工作时,会暂停所有应用线程(Stop The World),直到收集完成。这种"全世界暂停"的方式虽然简单粗暴,但在特定场景下却非常有效。

核心特点

  • 单线程工作,收集时暂停所有应用线程
  • 采用标记-复制算法,将Eden区和From Survivor区的存活对象复制到To Survivor区
  • 额外内存消耗最小,只需要维护一个线程的开销
  • 在单核处理器环境下效率很高,没有线程交互开销

配置参数

  • -XX:+UseSerialGC:启用Serial收集器
  • -XX:SurvivorRatio:Eden与Survivor区的比例
  • -XX:PretenureSizeThreshold:大对象直接进入老年代的阈值

适用场景

  • 客户端应用,如桌面程序、小程序
  • 小内存环境(几十到几百MB的新生代)
  • 单核或双核处理器环境
  • 对停顿时间不敏感的应用

优缺点

  • ✅ 优点:实现简单、内存开销小、单核环境下效率高
  • ❌ 缺点:停顿时间长、在多核环境下无法充分利用CPU资源

2. ParNew收集器 - "多人协作的清洁队"

工作方式 :多线程,Stop The World
算法 :标记-复制
适用场景:与CMS配合的服务端应用

ParNew就像是Serial的升级版,从单人作战变成了团队协作。但它仍然需要在工作时让所有人暂时离开房间。

详细介绍

ParNew收集器是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集外,其他行为与Serial收集器完全一致。它与Serial收集器共用了大量的代码,可以看作是Serial的并行化改造。

核心特点

  • 多线程并行收集,默认线程数与CPU核心数相同
  • 采用与Serial相同的标记-复制算法
  • 需要Stop The World,但停顿时间比Serial短
  • 与CMS收集器配合工作

配置参数

  • -XX:+UseParNewGC:启用ParNew收集器
  • -XX:ParallelGCThreads:设置垃圾收集线程数
  • -XX:SurvivorRatio:Eden与Survivor区的比例

适用场景

  • 与CMS收集器配合使用
  • 多核处理器的服务端应用
  • 对响应时间有一定要求的应用

工作流程

  1. 暂停所有应用线程(Stop The World)
  2. 多线程并行标记存活对象
  3. 多线程并行复制存活对象到Survivor区
  4. 清理Eden区和From Survivor区
  5. 恢复应用线程

优缺点

  • ✅ 优点:多线程并行收集,停顿时间比Serial短
  • ❌ 缺点:仍然需要Stop The World,单核环境下性能不如Serial

3. Parallel Scavenge收集器 - "效率至上的工厂流水线"

工作方式 :多线程,Stop The World
算法 :标记-复制
适用场景:后台运算,吞吐量优先应用

Parallel Scavenge不像是在打扫房间,更像是在优化工厂生产线。它不关心每次停顿多久,只关心单位时间内能完成多少工作。

详细介绍

Parallel Scavenge收集器是一款专注于吞吐量的新生代收集器。它的目标是达到一个可控制的吞吐量(Throughput),即处理器用于运行用户代码的时间与处理器总消耗时间的比值。

核心特点

  • 关注点是吞吐量而非停顿时间
  • 提供精确控制吞吐量的参数
  • 支持自适应调节策略
  • 采用多线程并行收集

配置参数

  • -XX:+UseParallelGC:启用Parallel Scavenge收集器
  • -XX:MaxGCPauseMillis:最大垃圾收集停顿时间
  • -XX:GCTimeRatio:垃圾收集时间与总时间的比率
  • -XX:+UseAdaptiveSizePolicy:启用自适应策略

自适应策略

当启用自适应策略后,虚拟机会根据系统运行情况自动调整以下参数:

  • 新生代大小(-Xmn)
  • Eden与Survivor区的比例(-XX:SurvivorRatio)
  • 晋升老年代对象大小(-XX:PretenureSizeThreshold)

适用场景

  • 后台运算任务,如批量处理、数据分析
  • 对吞吐量要求高于响应时间的应用
  • 不需要与用户交互的应用

优缺点

  • ✅ 优点:吞吐量高、支持自适应调节、适合后台运算
  • ❌ 缺点:停顿时间不可控、不适合交互式应用

老年代收集器

4. Serial Old收集器 - "老黄牛的晚年生活"

工作方式 :单线程,Stop The World
算法 :标记-整理
适用场景:客户端模式,CMS失败时的备胎

Serial Old就像是Serial收集器退休后再就业,虽然年纪大了,但依然兢兢业业。

详细介绍

Serial Old是Serial收集器的老年代版本,同样采用单线程工作和Stop The World的方式。它使用标记-整理算法,避免内存碎片的产生。

核心特点

  • 单线程工作,需要Stop The World
  • 采用标记-整理算法
  • 内存开销小
  • 作为CMS失败时的备胎

工作流程

  1. 标记阶段:标记所有存活对象
  2. 整理阶段:将所有存活对象向一端移动
  3. 清理阶段:清理边界以外的内存

适用场景

  • 客户端模式下的老年代收集
  • 与Parallel Scavenge搭配使用(JDK5及之前)
  • CMS收集器发生Concurrent Mode Failure时的后备预案

配置参数

  • -XX:+UseSerialGC:启用Serial Old收集器
  • 通常与其他收集器配合使用,不需要单独配置

优缺点

  • ✅ 优点:实现简单、内存开销小、不会产生内存碎片
  • ❌ 缺点:停顿时间长、单线程效率低

5. Parallel Old收集器 - "流水线的完美搭档"

工作方式 :多线程,Stop The World
算法 :标记-整理
适用场景:与Parallel Scavenge搭配的吞吐量优先应用

Parallel Old的出现让Parallel Scavenge终于有了门当户对的搭档,形成了真正的"吞吐量优先"组合。

详细介绍

Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现。它在JDK6中才开始提供,填补了Parallel Scavenge没有匹配老年代收集器的空白。

核心特点

  • 多线程并行收集
  • 采用标记-整理算法
  • 专注于吞吐量优化
  • 与Parallel Scavenge完美配合

工作流程

  1. 标记阶段:多线程并行标记存活对象
  2. 整理阶段:多线程并行整理内存
  3. 清理阶段:清理不可达对象

适用场景

  • 与Parallel Scavenge搭配使用
  • 注重吞吐量的应用
  • 后台运算任务

配置参数

  • -XX:+UseParallelOldGC:启用Parallel Old收集器
  • 通常与Parallel Scavenge一起使用

优缺点

  • ✅ 优点:吞吐量高、多线程并行、与Parallel Scavenge配合良好
  • ❌ 缺点:停顿时间较长、不适合交互式应用

6. CMS收集器 - "边开派对边打扫的管家"

工作方式 :并发,低停顿
算法 :标记-清除
适用场景:B/S系统,重视响应速度的应用

CMS就像是一位技艺高超的管家,能够在派对进行中悄悄打扫,尽量不打扰宾客的雅兴。

详细介绍

CMS(Concurrent Mark Sweep)收集器是一款以获取最短回收停顿时间为目标的收集器,非常适合重视响应速度的应用。它采用标记-清除算法,尽可能减少Stop The World的时间。

工作流程

  1. 初始标记:标记GC Roots直接关联的对象(需要Stop The World,但很快)
  2. 并发标记:并发遍历对象图,标记所有可达对象
  3. 重新标记:修正并发标记期间变动的标记记录(需要Stop The World)
  4. 并发清除:并发清理垃圾对象

核心特点

  • 并发收集,低停顿时间
  • 采用标记-清除算法,会产生内存碎片
  • 对处理器资源敏感
  • 无法处理"浮动垃圾"

配置参数

  • -XX:+UseConcMarkSweepGC:启用CMS收集器
  • -XX:CMSInitiatingOccupancyFraction:老年代使用率触发阈值
  • -XX:+UseCMSCompactAtFullCollection:Full GC时开启内存压缩
  • -XX:CMSFullGCsBeforeCompaction:设置多少次Full GC后压缩一次

浮动垃圾问题

由于CMS的并发清理阶段用户线程仍在运行,会产生新的垃圾对象,这些对象无法在本次收集中处理,称为"浮动垃圾"。

适用场景

  • B/S系统服务端
  • 重视响应速度的应用
  • 互联网应用

优缺点

  • ✅ 优点:并发收集、低停顿时间、适合交互式应用
  • ❌ 缺点:对CPU资源敏感、会产生内存碎片、无法处理浮动垃圾

全堆收集器:G1 - "智能分区清洁系统"

工作方式 :并发+并行
算法 :整体标记-整理,局部标记-复制
适用场景:大内存服务端应用,兼顾吞吐量和停顿时间

G1收集器就像是现代智能清洁系统,将房间分成多个区域,每次只清理最脏的区域,并且能够预测清理需要的时间。

详细介绍

G1(Garbage First)收集器是垃圾收集器技术发展历史上的里程碑成果,开创了面向局部收集的设计思路和基于Region的内存布局形式。它旨在替代CMS收集器,提供更可控的停顿时间。

革命性创新

  1. 基于Region的堆内存布局:将堆划分为多个大小相等的Region
  2. 可预测的停顿时间模型 :通过-XX:MaxGCPauseMillis参数设定期望停顿时间
  3. 面向局部收集:不再坚持固定分代,而是选择回收价值最大的Region
  4. Humongous区域:专门处理大对象

工作流程

  1. 初始标记:标记GC Roots直接关联的对象
  2. 并发标记:并发进行可达性分析
  3. 最终标记:处理并发标记期间的引用变动
  4. 筛选回收:根据回收价值排序,选择Region进行回收

配置参数

  • -XX:+UseG1GC:启用G1收集器
  • -XX:MaxGCPauseMillis:设置最大停顿时间目标
  • -XX:G1HeapRegionSize:设置Region大小
  • -XX:InitiatingHeapOccupancyPercent:触发并发标记周期的Java堆占用率阈值

适用场景

  • 大内存服务端应用(6GB以上)
  • 需要兼顾吞吐量和停顿时间的应用
  • 替代CMS收集器

优缺点

  • ✅ 优点:停顿时间可控、高吞吐量、不会产生内存碎片
  • ❌ 缺点:内存占用较高、写屏障实现复杂

如何选择收集器?

选择垃圾收集器就像选择清洁方式,需要根据实际情况决定:

  1. 小内存客户端应用(小于512MB)

    • Serial + Serial Old组合
    • 简单高效,内存开销小
  2. 重视响应速度的B/S系统

    • ParNew + CMS组合
    • 低停顿时间,适合交互式应用
  3. 后台运算,注重吞吐量

    • Parallel Scavenge + Parallel Old组合
    • 高吞吐量,适合批处理任务
  4. 大内存服务端应用(大于6GB)

    • G1收集器
    • 兼顾吞吐量和停顿时间
  5. 超大内存应用(大于32GB)

    • 考虑ZGC或Shenandoah
    • 极低停顿时间,适合超大内存

选择建议

  • 先明确应用需求:吞吐量优先还是响应时间优先
  • 根据堆内存大小选择
  • 在生产环境中进行实际测试验证
  • 随着JDK版本更新,优先考虑 newer 收集器

结语

Java垃圾收集器的发展历程就像清洁方式的进化:从最初需要全员离开的彻底打扫(Serial),到可以边工作边打扫的智能清洁(CMS),再到分区打扫、时间可控的现代清洁系统(G1)。每种收集器都有其适用场景,没有绝对的最好,只有最适合的。

随着JDK的持续发展,更新的收集器如ZGC、Shenandoah也在不断涌现,它们提供了更低的停顿时间和更好的性能表现。但理解这些基础收集器的工作原理,仍然是我们优化Java应用性能的基石。

希望本文能帮助你更好地理解Java垃圾收集器,选择适合的"清洁工",让你的Java应用跑得更加顺畅!记住,垃圾收集器的调优是一个持续的过程,需要根据实际应用特点和负载变化进行调整。

相关推荐
佛祖让我来巡山10 小时前
小区物业的智慧:轻松图解JVM垃圾回收的奥秘
gc·垃圾回收·安全区域·安全点·三色标记
boonya3 天前
Java垃圾回收机制理论算法及使用
jvm·算法·gc·垃圾收集器·理论
佛祖让我来巡山4 天前
深入理解JNI、安全点与循环优化:构建高健壮性Java应用
gc·jni·安全点
鼠鼠我捏,要死了捏6 天前
深入实践G1垃圾收集器调优:Java应用性能优化实战指南
java·g1·gc调优
怒码ing10 天前
垃圾回收,几种GC算法及GC机制
gc·垃圾回收算法·jvm内存管理
openHiTLS密码开源社区19 天前
【密码学基础】加密消息语法 CMS:给数字信息装个 “安全保险箱”
cms·加密·签名·验证·加密消息语法
没有bug.的程序员24 天前
GC 日志分析与调优:从日志到性能优化的实战指南
性能优化·gc·日志分析·gc调优
淡海水1 个月前
【原理】Unity GC 对比 C# GC
unity·c#·gc·垃圾回收
豆豆1 个月前
如何创建网站,网站建设几个基本流程
cms·网站建设·网站制作·网站模板·自助建站·建站步骤·建站流程