深入理解Java三大垃圾回收器:串行、吞吐量优先、响应时间优先

发布时间:2026/5/26 5:37:05

深入理解Java三大垃圾回收器:串行、吞吐量优先、响应时间优先 一、引言垃圾回收器是垃圾回收算法的具体实现不同回收器针对不同业务场景如延迟敏感、吞吐量优先做了优化。当前HotSpot虚拟机中主流回收器可分为三类串行回收器、吞吐量优先回收器、响应时间优先回收器CMS本文将逐一拆解它们的核心原理、执行流程、JVM参数与优缺点结合生产环境调优细节帮你彻底吃透回收器的底层逻辑。二、串行回收器Serial / Serial Old2.1 核心定义Serial是最基础、历史最悠久的垃圾回收器在JDK 1.3.1前是HotSpot新生代回收的唯一选择其老年代版本为Serial Old采用标记-整理算法。2.2 核心特点单线程执行仅用单个CPU/线程完成垃圾回收无法利用多核优势。新生代采用复制算法适合新生代“朝生夕死”的对象特性。新生代内存还是需要调大一些即使新生代采用复制算法需要移动对象复制算法性能效率较低但是大部分对象用过即死只有少数对象存活所以性能不会下降太多。必须STWStop-The-World回收时会暂停所有用户工作线程直到回收完成。适用场景堆内存较小如个人PC、客户端程序对延迟不敏感的场景。2.3 执行流程所有用户线程到达安全点后阻塞 → 单GC线程执行垃圾回收 → 回收完成后恢复所有用户线程。2.4 关键JVM参数-XX:UseSerialGC // 显式指定使用串行回收器新生代Serial 老年代Serial Old2.5 优缺点✅ 优点❌ 缺点实现简单、逻辑清晰内存开销极小无线程切换开销单核场景下效率较高单线程执行多核CPU利用率低必须全程STW停顿时间长不适合服务端高并发场景三、吞吐量优先回收器Parallel / Parallel Old3.1 核心定义Parallel回收器也叫“吞吐量优先回收器”是JDK 1.8的默认回收器核心目标是最大化单位时间内的业务吞吐量即CPU用于业务代码的时间占比。3.2 核心特点参数联动特性-XX:UseParallelGC新生代并行回收器复制算法与-XX:UseParallelOldGC老年代并行回收器标记/整理算法只需开启一个另一个会被自动启用。多线程并行回收默认GC线程数等于CPU核数可通过-XX:ParallelGCThreadsn手动控制内存不足触发GC时所有用户线程会先到达安全点后阻塞再由多GC线程并行执行回收。CPU使用特征GC执行时CPU使用率呈现“凸峰”形态——此时所有GC线程满负荷运行CPU占用率几乎100%凸峰位置对应GC执行时段。自适应调整能力-XX:UseAdaptiveSizePolicyParallelGC会自适应调整Eden区与Survivor区的内存比例同时对象晋升阈值也会受该参数影响。追求总STW最短比如1秒内发生2次0.2秒的STW总停顿0.4秒比5次0.1秒的STW总停顿0.5秒更优因为总停顿时间更短。适用场景堆内存较大、多核CPU的后台计算型服务如大数据处理、批处理任务。3.3 核心参数与目标冲突ParallelGC的核心调优参数围绕“吞吐量”和“单次STW时长”两个反向目标设计JVM会自适应调整堆大小以寻求折中1. 吞吐量目标-XX:GCTimeRatioratio计算公式目标吞吐量 1/(1ratio)默认值99即业务时间占比99%GC时间占比≤1%。工作逻辑若GC时间占比超过1%JVM会自动调大堆内存减少GC触发频率从而降低总GC时间占比。实战建议默认值99几乎无法实现生产环境一般将ratio设为19对应GC时间占比5%。2. 单次STW时长目标-XX:MaxGCPauseMillisms默认值200msJVM会尽量保证单次STW不超过该值。目标冲突堆内存越大单次STW时长越长回收范围更大堆内存越小GC触发越频繁总GC时间占比会超标无法满足GCTimeRatio。核心逻辑ParallelGC会在两个反向目标间动态调整堆大小最终取折中方案。3.4 执行流程内存不足了就会让所有用户线程到达安全点后停下来开始垃圾回收即 所有用户线程到达安全点后阻塞 → 多GC线程默认CPU核数并行执行垃圾回收 → 回收完成后恢复所有用户线程。3.5 关键JVM参数汇总-XX:UseParallelGC // 启用Parallel回收器新生代Parallel 老年代Parallel OldJDK1.8默认开启 -XX:UseParallelOldGC // 显式启用老年代并行回收器开启后自动启用UseParallelGC -XX:UseAdaptiveSizePolicy // 自适应调整Eden/Survivor比例影响对象晋升阈值 -XX:GCTimeRatioratio // 目标吞吐量默认99实战建议设为19 -XX:MaxGCPauseMillisms // 目标最大单次STW时间默认200ms -XX:ParallelGCThreadsn // 指定GC并行线程数默认等于CPU核心数3.6 优缺点✅ 优点❌ 缺点多核CPU利用率高总STW时间短吞吐量高自适应策略可自动优化内存分配无需手动精细调优CPU利用率接近100%GC执行效率高单次STW时长可能比CMS更长不适合延迟敏感的服务无法像CMS一样做到并发回收全程仍需STW吞吐量与单次STW时长目标冲突需手动折中调参四、响应时间优先回收器CMSConcurrent Mark Sweep4.1 核心定义CMS是一款老年代回收器需配合新生代ParNew回收器核心目标是最小化单次STW时长让垃圾回收对用户请求的延迟影响降到最低适合互联网电商、网关等延迟敏感型服务。4.2 核心特点并发标记并发清除大部分回收阶段与用户线程并行执行仅短暂STW。采用标记-清除算法避免移动对象的开销但会产生内存碎片核心隐患。追求单次STW最短比如将单次STW控制在0.1秒内即使总停顿次数更多用户也几乎无感知。CPU占用特征GC线程数仅为CPU核数的1/4远低于ParallelGC的100%占用因此吞吐量会下降。4.3 执行流程4个核心阶段1. 初始标记STW极短仅标记GC Roots直接关联的对象停顿时间可忽略不计。受参数影响ParallelGCThreadsn默认CPU核数控制STW阶段并行线程数、ConcGCThreadsn默认ParallelGCThreads的1/4控制并发阶段线程数。2. 并发标记无STW与用户线程并行遍历GC Roots间接关联的对象不STW。核心问题用户线程运行过程中会产生新对象、修改对象引用对垃圾标记结果造成干扰。3. 重新标记STW短核心目的修正并发标记阶段因用户线程干扰产生的标记错误。关键优化参数-XX:CMSScavengeBeforeRemark问题背景重新标记需扫描整个堆通过新生代对象可达性分析老年代对象若新生代存在大量待回收对象会产生无用扫描操作影响性能。优化逻辑重新标记前先执行一次Minor GC清理新生代垃圾减少扫描对象数量降低重新标记的性能压力。4. 并发清除无STW与用户线程并行清除标记的垃圾对象不STW。核心问题此阶段用户线程产生的新垃圾无法被清理需等到下一次GC这类垃圾称为浮动垃圾。4.4 核心痛点与解决方案4.4.1 浮动垃圾与预留空间产生原因并发清除阶段用户线程仍在运行会产生新的垃圾浮动垃圾无法即时清理。核心风险若等到堆内存不足再触发GC浮动垃圾会无空间存放导致OOM。解决方案通过-XX:CMSInitiatingOccupancyFractionpercent控制GC触发时机——老年代内存占比达到该值时触发CMS回收预留空间存放浮动垃圾默认值92%。4.4.2 内存碎片与并发失败核心隐患CMS采用标记-清除算法长期运行会产生大量内存碎片。并发失败场景内存碎片过多时Minor GC后存活对象无法晋升到老年代无连续内存空间导致CMS无法正常工作。补救措施CMS会退化到Serial Old单线程回收器执行一次全堆标记-整理操作。严重后果Serial Old为单线程执行GC时间会瞬间飙升完全违背CMS“响应时间优先”的设计目标CMS最大问题。4.4.3 并发失败的其他场景除内存碎片外若CMS执行过程中老年代空间不足也会触发并发失败此时同样会退化到Serial Old单线程回收。4.5 CMS老年代内存大小的核心误区与调优逻辑4.5.1 先澄清核心误区CMS的“老年代内存越大”≠STW更长“内存越大STW越长”主要是针对标记-整理算法比如Serial Old、Parallel Old这类算法需要移动存活对象内存越大、存活对象越多STW时间确实会更长。但CMSConcurrent Mark Sweep是“并发清除”算法它的STW阶段非常短且核心逻辑和“整理算法”完全不同CMS的STW只发生在初始标记和重新标记两个阶段这两个阶段只扫描GC Roots和直接关联对象和老年代总内存大小弱相关真正耗时的并发标记和并发清除阶段是和用户线程一起跑的不会STW。4.5.2 为什么CMS老年代内存越大反而更好1. 避免“Concurrent Mode Failure”并发模式失败CMS是并发回收在回收老年代的同时用户线程还在不断往老年代分配对象“浮动垃圾”。如果老年代内存太小并发回收还没做完老年代就满了 → 触发Concurrent Mode Failure→ 退化为Serial Old单线程标记-整理这时候会发生长时间STW严重影响响应时间。如果老年代内存更大有更多空间容纳浮动垃圾降低并发模式失败概率降低GC频率减少整体STW次数并发阶段有更充裕的时间完成清理避免退化成全STW的整理算法。2. 内存碎片的影响更小CMS是标记-清除算法会产生内存碎片。老年代越大碎片对“大对象分配”的影响越小更难提前触发Full GC更大的内存也能推迟“必须做碎片整理”的时间点减少整理带来的STW。4.5.3 和“响应时间优先”的目标是否矛盾完全不矛盾反而更契合响应时间优先CMS的设计目标就是低延迟、响应时间优先核心是把大部分GC工作放到并发阶段尽量缩短STW老年代内存更大 → 更少触发Full GC → 更少出现“并发模式失败” → 更少出现长时间STW → 整体响应时间更稳定、更短虽然理论上“重新标记”阶段会随存活对象增多略有变长但这个阶段本身耗时极短通常毫秒级远小于“并发模式失败”后Serial Old带来的秒级STW。4.5.4 总结一句话✅ CMS老年代内存越大越好本质是用更大的内存空间换取更低的GC频率更少的并发模式失败避免退化成长时间STW的标记-整理算法最终实现更稳定、更短的响应时间。这和“响应时间优先”的目标是一致的只是需要在内存容量和单次STW微增之间做权衡——而在实践中避免并发模式失败的收益远大于单次STW的微小增长。4.6 CPU占用与吞吐量CMS的GC线程数由ConcGCThreadsn控制默认ParallelGCThreads的1/4仅占用1/4 CPU核数执行GC剩余3/4核数留给用户线程。对比ParallelGCCMS的CPU利用率远低于ParallelGC前者≈25%后者≈100%因此CMS虽实现了低延迟但吞吐量会显著下降。4.7 关键JVM参数汇总-XX:UseConcMarkSweepGC // 启用CMS回收器新生代ParNew 老年代CMS -XX:ParallelGCThreadsn // 并行GC线程数STW阶段使用默认CPU核数 -XX:ConcGCThreadsn // 并发GC线程数默认ParallelGCThreads的1/4 -XX:CMSInitiatingOccupancyFractionpercent // 老年代内存占比阈值触发CMS回收默认92%预留浮动垃圾空间 -XX:CMSScavengeBeforeRemark // 重新标记前执行Minor GC减少扫描压力4.8 优缺点✅ 优点❌ 缺点单次STW时间极短延迟敏感场景体验极佳大部分阶段并发执行不影响用户线程响应CPU占用率低对业务线程影响小可通过参数精细调优适配不同延迟要求采用标记-清除算法产生内存碎片易触发并发失败并发失败时退化到Serial OldGC时间飙升并发阶段产生浮动垃圾需预留内存空间并发阶段消耗CPU资源吞吐量比ParallelGC低五、三大回收器核心对比吞吐量优先回收器 vs 响应时间优先回收器核心区别吞吐量优先ParallelGC追求单位时间内业务执行量最大GC 时会全程 STW、CPU 全力运行一次停顿时间较长但 GC 次数少、整体效率高适合后台计算、批处理等不敏感延迟的场景。响应时间优先CMS追求单次停顿时间最短大部分回收过程与业务线程并发执行仅短暂 STWCPU 占用低、体验流畅但会产生浮动垃圾与内存碎片并发失败时会退化适合网关、电商接口等低延迟要求的场景。回收器类型线程模型核心算法核心目标STW特点CPU占用率核心隐患适用场景串行Serial单线程复制/标记-整理简单、低内存开销全程STW停顿时间长单核100%无个人PC、客户端、小堆场景吞吐量优先Parallel多线程并行复制/标记-整理最大化单位时间吞吐量全程STW总停顿时间短接近100%目标冲突后台计算、批处理、大数据服务响应时间优先CMS多线程并发标记-清除最小化单次STW时长仅初始/重新标记STW极短约25%内存碎片/并发失败电商、网关等延迟敏感型服务六、总结ParallelGCJDK1.8默认回收器核心追求吞吐量UseParallelGC与UseParallelOldGC参数联动需在吞吐量GCTimeRatio和单次STW时长MaxGCPauseMillis间折中调参实战建议将GCTimeRatio设为19。CMS延迟敏感场景首选通过并发回收降低单次STW但存在浮动垃圾、内存碎片问题并发失败时会退化到Serial Old导致STW飙升是其最大缺陷CMSScavengeBeforeRemark可优化重新标记性能CMSInitiatingOccupancyFraction需预留浮动垃圾空间且CMS老年代内存越大越有利于降低并发失败概率契合响应时间优先目标。核心取舍ParallelGC牺牲单次延迟换吞吐量CMS牺牲吞吐量换单次延迟需根据业务场景选择无“最优回收器”只有“最适配回收器”。

相关新闻