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

新生代收集器
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收集器配合使用
-
多核处理器的服务端应用
-
对响应时间有一定要求的应用
工作流程:
-
暂停所有应用线程(Stop The World)
-
多线程并行标记存活对象
-
多线程并行复制存活对象到Survivor区
-
清理Eden区和From Survivor区
-
恢复应用线程
优缺点:
-
✅ 优点:多线程并行收集,停顿时间比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失败时的备胎
工作流程:
-
标记阶段:标记所有存活对象
-
整理阶段:将所有存活对象向一端移动
-
清理阶段:清理边界以外的内存
适用场景:
-
客户端模式下的老年代收集
-
与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完美配合
工作流程:
-
标记阶段:多线程并行标记存活对象
-
整理阶段:多线程并行整理内存
-
清理阶段:清理不可达对象
适用场景:
-
与Parallel Scavenge搭配使用
-
注重吞吐量的应用
-
后台运算任务
配置参数:
-
-XX:+UseParallelOldGC:启用Parallel Old收集器 -
通常与Parallel Scavenge一起使用
优缺点:
-
✅ 优点:吞吐量高、多线程并行、与Parallel Scavenge配合良好
-
❌ 缺点:停顿时间较长、不适合交互式应用
6. CMS收集器 - "边开派对边打扫的管家"
工作方式 :并发,低停顿算法 :标记-清除适用场景:B/S系统,重视响应速度的应用
CMS就像是一位技艺高超的管家,能够在派对进行中悄悄打扫,尽量不打扰宾客的雅兴。
详细介绍:CMS(Concurrent Mark Sweep)收集器是一款以获取最短回收停顿时间为目标的收集器,非常适合重视响应速度的应用。它采用标记-清除算法,尽可能减少Stop The World的时间。
工作流程:
-
初始标记:标记GC Roots直接关联的对象(需要Stop The World,但很快)
-
并发标记:并发遍历对象图,标记所有可达对象
-
重新标记:修正并发标记期间变动的标记记录(需要Stop The World)
-
并发清除:并发清理垃圾对象
核心特点:
-
并发收集,低停顿时间
-
采用标记-清除算法,会产生内存碎片
-
对处理器资源敏感
-
无法处理"浮动垃圾"
配置参数:
-
-XX:+UseConcMarkSweepGC:启用CMS收集器 -
-XX:CMSInitiatingOccupancyFraction:老年代使用率触发阈值 -
-XX:+UseCMSCompactAtFullCollection:Full GC时开启内存压缩 -
-XX:CMSFullGCsBeforeCompaction:设置多少次Full GC后压缩一次
浮动垃圾问题:由于CMS的并发清理阶段用户线程仍在运行,会产生新的垃圾对象,这些对象无法在本次收集中处理,称为"浮动垃圾"。
适用场景:
-
B/S系统服务端
-
重视响应速度的应用
-
互联网应用
优缺点:
-
✅ 优点:并发收集、低停顿时间、适合交互式应用
-
❌ 缺点:对CPU资源敏感、会产生内存碎片、无法处理浮动垃圾
全堆收集器:G1 - "智能分区清洁系统"
工作方式 :并发+并行算法 :整体标记-整理,局部标记-复制适用场景:大内存服务端应用,兼顾吞吐量和停顿时间
G1收集器就像是现代智能清洁系统,将房间分成多个区域,每次只清理最脏的区域,并且能够预测清理需要的时间。
详细介绍:G1(Garbage First)收集器是垃圾收集器技术发展历史上的里程碑成果,开创了面向局部收集的设计思路和基于Region的内存布局形式。它旨在替代CMS收集器,提供更可控的停顿时间。
革命性创新:
-
基于Region的堆内存布局:将堆划分为多个大小相等的Region
-
可预测的停顿时间模型 :通过
-XX:MaxGCPauseMillis参数设定期望停顿时间 -
面向局部收集:不再坚持固定分代,而是选择回收价值最大的Region
-
Humongous区域:专门处理大对象
工作流程:
-
初始标记:标记GC Roots直接关联的对象
-
并发标记:并发进行可达性分析
-
最终标记:处理并发标记期间的引用变动
-
筛选回收:根据回收价值排序,选择Region进行回收
配置参数:
-
-XX:+UseG1GC:启用G1收集器 -
-XX:MaxGCPauseMillis:设置最大停顿时间目标 -
-XX:G1HeapRegionSize:设置Region大小 -
-XX:InitiatingHeapOccupancyPercent:触发并发标记周期的Java堆占用率阈值
适用场景:
-
大内存服务端应用(6GB以上)
-
需要兼顾吞吐量和停顿时间的应用
-
替代CMS收集器
优缺点:
-
✅ 优点:停顿时间可控、高吞吐量、不会产生内存碎片
-
❌ 缺点:内存占用较高、写屏障实现复杂
如何选择收集器?
选择垃圾收集器就像选择清洁方式,需要根据实际情况决定:
-
小内存客户端应用(小于512MB)
Serial + Serial Old组合
简单高效,内存开销小
-
重视响应速度的B/S系统
ParNew + CMS组合
低停顿时间,适合交互式应用
-
后台运算,注重吞吐量
Parallel Scavenge + Parallel Old组合
高吞吐量,适合批处理任务
-
大内存服务端应用(大于6GB)
G1收集器
兼顾吞吐量和停顿时间
-
超大内存应用(大于32GB)
考虑ZGC或Shenandoah
极低停顿时间,适合超大内存
选择建议:
-
先明确应用需求:吞吐量优先还是响应时间优先
-
根据堆内存大小选择
-
在生产环境中进行实际测试验证
-
随着JDK版本更新,优先考虑 newer 收集器
结语
Java垃圾收集器的发展历程就像清洁方式的进化:从最初需要全员离开的彻底打扫(Serial),到可以边工作边打扫的智能清洁(CMS),再到分区打扫、时间可控的现代清洁系统(G1)。每种收集器都有其适用场景,没有绝对的最好,只有最适合的。
随着JDK的持续发展,更新的收集器如ZGC、Shenandoah也在不断涌现,它们提供了更低的停顿时间和更好的性能表现。但理解这些基础收集器的工作原理,仍然是我们优化Java应用性能的基石。
希望本文能帮助你更好地理解Java垃圾收集器,选择适合的"清洁工",让你的Java应用跑得更加顺畅!记住,垃圾收集器的调优是一个持续的过程,需要根据实际应用特点和负载变化进行调整。
文章转载自: ++佛祖让我来巡山++