虚拟机GC算法是Java虚拟机(JVM)内存管理的核心机制,其设计目标是通过自动回收不再使用的对象,解决内存泄漏和溢出问题,同时确保程序运行的效率与稳定性,随着Java版本的迭代,GC算法经历了从简单到复杂、从低效到高效的演进过程,形成了多样化的技术方案,以适应不同场景下的性能需求,本文将系统梳理主流GC算法的原理、分类及优化方向,帮助读者理解虚拟机内存管理的底层逻辑。

GC算法的核心目标与基础原理
GC算法的设计始终围绕三个核心目标:内存回收的高效性(减少停顿时间)、吞吐量(单位时间内程序运行时间占比)和低延迟(避免长时间卡顿),其基础原理基于“可达性分析”算法:通过一系列称为“GC Roots”的对象(如虚拟机栈中引用的对象、静态变量等)作为起点,遍历所有引用链,未被触及的对象即为不可达,可判定为垃圾,这一过程需要解决三个关键问题:如何定位垃圾(垃圾判定算法)、如何回收垃圾(回收算法)以及如何回收时减少对应用的影响(回收器实现)。
垃圾判定算法:标记与引用的博弈
在GC算法中,判定对象是否为“垃圾”是第一步,主流判定方法包括引用计数法和可达性分析法。
引用计数法通过为每个对象添加一个引用计数器,每当有一个地方引用该对象时计数器加1,引用失效时减1,计数器为0即判定为垃圾,该方法实现简单,但存在致命缺陷:无法处理循环引用问题(如两个对象相互引用,即使外部无引用,计数器也无法归零),因此在主流虚拟机中未被采用。
可达性分析法是目前工业界的标准方案,其核心思想是从GC Roots出发,通过“根搜索”算法标记所有可达对象,剩余未标记对象即为垃圾,该方法避免了循环引用的干扰,但需要暂停所有用户线程(即“Stop-The-World”,STW),这是GC导致应用停顿的主要原因之一。
经典GC回收算法:分代理论的实践
为提升回收效率,JVM根据对象存活周期将堆内存划分为新生代(Young Generation)和老年代(Old Generation),不同区域采用不同的回收算法,这一思想被称为“分代收集理论”。
标记-清除算法(Mark-Sweep)
标记-清除算法是最基础的回收算法,分为两个阶段:标记(遍历GC Roots,标记所有存活对象)和清除(遍历堆内存,回收未被标记的对象),该算法实现简单,但存在两个显著缺点:一是空间碎片化,回收后内存空间不连续,可能导致大对象无法分配;二是效率不稳定,当堆内存较大时,标记和清除过程耗时较长。
标记-复制算法(Mark-Copy)
为解决碎片化问题,标记-复制算法将新生代划分为一块Eden区和两块Survivor区(From和To,默认比例8:1:1),每次只使用Eden区和一块Survivor区,当空间不足时,将存活对象复制到另一块Survivor区,并清空原区域,该算法避免了内存碎片,且复制过程只涉及存活对象,当对象存活率低时效率极高,但缺点也很明显:空间利用率低(可用内存仅为新生代容量的90%),且当对象存活率较高时,复制成本会急剧上升。

标记-整理算法(Mark-Compact)
标记-整理算法结合了标记-清除和标记-复制的优点,分为标记(同标记-清除)和整理(将所有存活对象向内存空间一端移动,直接清理端外内存),该算法解决了碎片化问题,且不需要额外空间,但移动对象并更新引用指针的操作较为耗时,通常用于老年代回收。
分代收集理论的落地
分代收集理论将上述算法结合:新生代(对象存活时间短)采用标记-复制算法(Minor GC),老年代(对象存活时间长)采用标记-清除或标记-整理算法(Major GC/Full GC),这种设计兼顾了回收效率与内存利用率,成为现代JVM的默认内存管理策略。
现代GC回收器:低延迟与高吞吐的平衡
随着应用对性能要求的提升,现代GC回收器在分代理论的基础上,通过并发、并行等技术优化STW时间,形成了多样化的解决方案。
串行回收器(Serial GC)
串行回收器是最古老的回收器,采用单线程进行GC工作,且在回收时必须暂停所有用户线程,其优点是内存占用小、算法简单,适合单核CPU或内存较小的客户端场景,新生代采用标记-复制,老年代采用标记-整理。
吞吐优先回收器(Throughput Collector,-XX:+UseParallelGC)
并行回收器(也称“吞吐量优先回收器”)通过多线程并行执行GC任务,大幅缩短STW时间,目标是最大化应用运行时间占比(吞吐量),新生代采用并行标记-复制(Parallel Scavenge),老年代采用并行标记-整理(Parallel Old),适合后台计算等对延迟不敏感的场景,如大数据处理。
并发回收器(Concurrent Mark Sweep,CMS,-XX:+UseConcMarkSweepGC)
CMS是第一款真正意义上的并发回收器,以“低延迟”为核心目标,其回收过程分为四个阶段:初始标记(STW)、并发标记(与用户线程并发)、重新标记(STW,修正并发标记期间变动的对象)和并发清除(与用户线程并发),CMS的并发阶段大幅减少了STW时间,但存在三个缺点:对CPU资源敏感(并发阶段会占用线程资源)、产生内存碎片(基于标记-清除算法)、“并发模式失败”风险(并发清理时内存不足触发Full GC)。
G1回收器(Garbage-First,-XX:+UseG1GC)
G1是面向服务端应用的回收器,其设计目标是“可预测的停顿时间”,它打破了物理分代界限,将堆划分为多个大小相等的独立Region(每个Region可扮演Eden、Survivor或Old角色),并跟踪每个Region的回收价值(回收所释放的空间与时间的比),在每次回收时,优先回收价值最大的Region(即“Garbage-First”),G1的回收过程包括初始标记、并发标记、最终标记和筛选回收(STW),通过Region化设计和混合回收(同时处理新生代与老年代)实现了低延迟与高吞吐的平衡,是目前JDK 9及之后的默认回收器。

ZGC与Shenandoah:超低延迟的探索
ZGC(-XX:+UseZGC)和Shenandoah是新一代并发回收器,目标是将STW时间控制在毫秒甚至亚毫秒级,ZGC通过着色指针(Colored Pointer)和读屏障(Read Barrier)实现并发标记、并发转移,几乎全程不STW;Shenandoah则通过并发转移(Concurrent Evacuation)减少停顿,两者均适合超大内存(TB级)和低延迟敏感场景(如金融交易、实时计算)。
GC算法的优化与选择
GC算法的优化方向主要包括:减少STW时间(通过并发、并行技术)、降低内存碎片(通过整理、复制策略)和提升回收效率(通过分代、Region化设计),在实际应用中,选择合适的GC回收器需结合业务场景:
- 客户端应用:优先选择串行回收器,内存占用小,启动快;
- 后台计算服务:选择吞吐优先回收器,最大化CPU利用率;
- 在线服务:选择G1、ZGC或Shenandoah,平衡延迟与吞吐;
- 超大内存应用:ZGC或Shenandoah是更优解,避免Full GC导致的长停顿。
通过JVM参数(如-XX:MaxGCPauseMillis、-XX:G1HeapRegionSize)调优,结合内存监控工具(如JConsole、VisualVM、GCEasy),可进一步优化GC性能。
虚拟机GC算法的演进,本质上是内存管理效率与应用性能需求的持续博弈,从简单的标记-清除到复杂的并发回收器,从分代理论到Region化设计,GC算法在自动化、低延迟、高吞吐的方向上不断突破,理解GC算法的原理与优化逻辑,不仅能帮助开发者排查内存泄漏、调优JVM参数,更能深入把握Java程序的底层运行机制,为构建高性能、高可用的应用系统奠定基础,随着云原生和Serverless架构的普及,GC算法仍将朝着更智能、更自适应的方向发展,持续为软件开发提供坚实的内存管理支撑。















