目录
[1. 内存分区:将可用内存划分为大小相等的两块](#1. 内存分区:将可用内存划分为大小相等的两块)
[2. 垃圾回收:标记存活对象并复制到空闲区](#2. 垃圾回收:标记存活对象并复制到空闲区)
[3. 区域切换:清空原区域,互换 From/To 角色](#3. 区域切换:清空原区域,互换 From/To 角色)
[二、复制算法的流程示例(以新生代 Eden+S0→S1 为例)](#二、复制算法的流程示例(以新生代 Eden+S0→S1 为例))
[1. 优点(核心优势:高效、无碎片)](#1. 优点(核心优势:高效、无碎片))
[2. 缺点(核心局限:内存利用率低、复制开销)](#2. 缺点(核心局限:内存利用率低、复制开销))
复制算法的核心思路是将内存划分为两块区域,只使用其中一块,垃圾回收时将存活对象复制到另一块,然后清空原区域,以此避免内存碎片、提升回收效率。
一、复制算法的核心工作流程
复制算法的设计初衷是解决 "标记 - 清除算法" 的内存碎片问题,其核心是内存分区 + 存活对象复制 + 原区域清空,具体流程分 3 步(以经典的 "半区复制" 为例):
1. 内存分区:将可用内存划分为大小相等的两块
首先,将堆中某块区域(如新生代)划分为From 区(使用区) 和To 区(空闲区) ,两块区域大小完全相同,且同一时间只使用 From 区,To 区保持空闲。
补充:现代 JVM(如 HotSpot)对新生代的优化:将新生代划分为 1 个 Eden 区 + 2 个 Survivor 区(S0、S1),Eden 区占比约 80%,S0/S1 各占 10%,本质仍是复制算法的变种(Eden+S0 为 "From 区",S1 为 "To 区")。
2. 垃圾回收:标记存活对象并复制到空闲区
当 From 区内存耗尽触发 GC 时,执行两步核心操作:
- 标记存活对象:遍历 From 区,标记所有被引用的存活对象(如被栈中引用、老年代引用的对象);
- 复制存活对象 :将所有标记的存活对象按内存地址连续复制 到 To 区(空闲区),且复制后的对象在 To 区是连续存储的,无内存碎片。
3. 区域切换:清空原区域,互换 From/To 角色
- 复制完成后,清空 From 区的所有数据(直接释放整个区域,无需逐个清理垃圾);
- 将 From 区和 To 区的角色互换:原 To 区变为新的 From 区(下次分配内存使用),原 From 区变为新的 To 区(下次 GC 的空闲区)。
二、复制算法的流程示例(以新生代 Eden+S0→S1 为例)
为了更直观理解,以 HotSpot 新生代的复制流程为例:
- 初始状态:Eden 区和 S0 区(From 区)用于分配新对象,S1 区(To 区)空闲;
- Eden 区满触发 Minor GC:
- 标记 Eden+S0 中所有存活对象;
- 将存活对象复制到 S1 区(连续存储);
- 清空 Eden 区和 S0 区;
- 角色切换:S1 变为新的 From 区(下次对象分配到 Eden+S1),S0 变为新的 To 区(空闲);
- 下次 GC 时,重复上述流程,存活对象在 S0/S1 之间来回复制,达到年龄阈值(如 15)的对象会被复制到老年代。
三、复制算法的优缺点分析
1. 优点(核心优势:高效、无碎片)
- 回收效率极高 :① 只需要遍历并复制存活对象,无需处理垃圾对象(垃圾对象直接随原区域清空);② 新生代中 90% 以上的对象都是 "朝生夕死",存活对象极少,复制成本极低;
- 无内存碎片 :存活对象被连续复制到空闲区,分配新对象时只需按指针偏移即可(如
指针碰撞方式),无需维护空闲列表,分配效率高; - 实现简单:逻辑比标记 - 清除、标记 - 整理算法更简单,易于工程实现。
2. 缺点(核心局限:内存利用率低、复制开销)
- 内存利用率低:经典半区复制算法仅能使用 50% 的内存(另一块始终空闲);即使是 HotSpot 的 Eden+2Survivor 设计,也仅能使用约 90% 的新生代内存(1 个 Survivor 始终空闲),本质仍是 "牺牲内存换效率";
- 有复制开销:若存活对象比例较高(如老年代),复制大量存活对象会消耗 CPU 资源,导致 GC 停顿时间变长;
- 需要对象年龄记录:为了将长期存活的对象转移到老年代,需要为每个对象维护 "年龄计数器",增加了少量内存开销;
- 无法直接用于老年代:老年代对象存活率高、体积大,若用复制算法会产生大量复制开销,且老年代内存空间大,预留一半空闲区的成本过高。
四、复制算法的适用场景
复制算法的优缺点决定了其仅适合新生代 GC(Minor GC),原因:
- 新生代对象存活时间短、存活率低(通常 < 10%),复制开销可忽略;
- 新生代内存空间小,预留空闲区的内存损耗可接受;
- 无碎片的特性适配新生代高频次的对象分配需求。
而老年代 GC(Major GC/Full GC)则主要使用标记 - 整理算法(或标记 - 清除 + 标记 - 整理混合),避免复制开销和内存浪费。
总结
核心工作流程
- 内存划分为 "使用区(From)" 和 "空闲区(To)",仅使用 From 区;
- GC 时标记 From 区存活对象,复制到 To 区(连续存储);
- 清空 From 区,互换 From/To 角色,完成回收。
核心优缺点
表格
| 维度 | 优点 | 缺点 |
|---|---|---|
| 效率 | 回收 / 分配效率高,仅处理少量存活对象 | 存活对象多时有复制开销,GC 停顿变长 |
| 内存利用 | 无内存碎片,分配对象无需维护空闲列表 | 内存利用率低(经典版仅 50%) |
| 适用场景 | 新生代(存活率低、内存小) | 不适合老年代(存活率高、内存大) |
关键特征
复制算法是 "空间换时间" 的典型设计,通过牺牲部分内存的利用率,换取了高效、无碎片的垃圾回收效果,是新生代 GC 的首选算法。