标记清除适合老年代,因存活对象多、不移动对象节省开销且空间利用率高,但会产生内存碎片;复制算法专用于年轻代,因对象存活率低,可高效复制存活对象并消除碎片,但需预留空间。

标记清除和复制算法不是“哪个更好”的选择题,而是分工明确、各司其职的两种策略——关键看用在哪儿、对象活得久不久、停顿能不能忍。
标记清除适合老年代:存活对象多、移动成本高
老年代里对象普遍“活久见”,比如缓存、单例、长生命周期的业务对象。标记清除只做两件事:先从GC Roots出发标记所有可达对象,再统一回收没被标记的内存。它不挪动对象,省了复制开销,空间利用率接近100%。
- 优点:不用搬数据,对大对象友好;内存不浪费,适合长期驻留场景
- 缺点:清完留下一堆小碎片,万一来个8MB字节数组,就可能因找不到连续空间而直接触发Full GC
- 典型搭配:CMS(已弃用)、Serial Old、Parallel Old 默认都用标记-整理(它的升级版),但底层逻辑仍继承自标记清除思路
复制算法专治年轻代:对象死得快、搬得轻松
新生代95%以上对象活不过一次Minor GC,复制算法正是为这种“朝生暮死”量身定制。它把内存切成Eden + 两个Survivor(比如8:1:1),GC时只把Eden和一个Survivor里活着的对象拷到另一个Survivor,然后整块清空——天然紧凑、无碎片、清理极快。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 优点:一次搬运+整页清零,效率高;绝对不产生碎片;天然支持对象年龄晋升
- 缺点:总得空着一块区域备用,理论空间利用率最多50%(实际通过Eden占比优化)
- 注意点:Survivor太小,活对象没地方放,只能提前进老年代;MaxTenuringThreshold设太高,短命对象反复复制,白耗CPU
别硬套,分代才是底层逻辑
JVM不是拿一个算法打天下,而是按对象年龄分代管理:年轻代用复制(G1/ZGC早期阶段也用类似思路),老年代避开复制(搬不动、不划算),改用标记-整理或并发标记清除。G1的混合回收本质是“挑几块Region做精细复制”,ZGC和Shenandoah则绕开传统算法,靠读屏障+染色指针实现几乎不暂停的并发整理。
- 选算法,核心看三点:对象平均存活率、可接受的GC停顿时间、堆内存是否分代
- 把复制硬塞进老年代?等于让快递员每天扛着冰箱跑五公里——吞吐暴跌,GC时间翻倍
- 在年轻代用标记清除?碎片几天就堆满,Minor GC越来越频繁,很快拖垮系统
实际调优更看重组合效果
单独比较算法本身意义不大。真正影响性能的是整个回收链路:比如ParNew(复制)配CMS(标记清除+并发清理)曾是经典组合;现在G1默认年轻代复制、老年代标记整理;ZGC全程不区分代,但靠硬件特性达成毫秒级停顿。
- 观察指标优先看:GC频率、单次停顿时间、晋升到老年代的速度、Full GC是否频繁
- 调参方向不是换算法,而是匹配行为——比如SurvivorRatio调小一点,给复制留足缓冲;-XX:MaxTenuringThreshold设为6,避免过早晋升
- 新项目建议直接用G1或ZGC,它们自动适配不同区域,不用手动纠结用哪个算法
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










