复制算法天然避免内存碎片,根本原因是将存活对象整体搬迁至连续空闲区并批量清空原区,不进行零散释放或合并;年轻代采用eden+s0+s1三区结构,minor gc时从eden和当前survivor中复制存活对象至另一survivor,对象线性排列、地址连续,原区整块归还。

Java 年轻代(Young Generation)使用复制算法(Copying Algorithm)实现垃圾回收,天然避免内存碎片,根本原因在于:它不直接清理对象,而是将存活对象整体搬移到另一块连续空闲区域,原区域直接清空归还——整个过程不涉及“打孔”或“合并”,所以不会产生碎片。
复制算法的核心机制:From 和 To 区交替使用
年轻代通常划分为一个 Eden 区和两个大小相等的 Survivor 区(S0 和 S1),合称“半区复制”结构。每次 Minor GC 时:
- 从 Eden + 当前使用的 Survivor(如 S0)中识别出所有存活对象
- 将这些存活对象**一次性、顺序地复制**到另一个空闲 Survivor 区(如 S1)的起始位置
- 复制完成后,Eden 和 S0 被整体清空;S1 成为新的“目标 Survivor”,S0 变为空闲待用区
- 对象在 S1 中按复制顺序紧密排列,地址连续、无间隙
为什么没有碎片?关键在“整块搬迁 + 批量清空”
与标记-清除(Mark-Sweep)不同,复制算法不保留原内存布局,也不尝试就地释放零散空间:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 存活对象被集中复制到新区域的**线性内存段**中,类似数组追加写入
- 原 Eden 和 From Survivor 区在 GC 后变成一块完整的空闲大块内存,可直接用于下一次对象分配(TLAB 或直接分配)
- 即使某些对象生命周期极短(如临时变量),它们占用的空间也随整个区域清空而一并释放,不留下“小洞”
大对象和年龄阈值如何适配复制逻辑?
复制算法对对象大小和年龄做了针对性处理,确保不破坏零碎片特性:
- 超过一定大小(如超过 Eden 的一半)的对象,JVM 直接分配到老年代(通过 -XX:PretenureSizeThreshold 控制),绕过年轻代复制流程
- 在 Survivor 区中经历多次 GC 仍存活的对象(达到 -XX:MaxTenuringThreshold,默认 15),会被晋升至老年代,不再参与年轻代复制
- Survivor 区本身只承担“中转”角色,容量固定且较小,复制压力可控;只要存活对象总大小 ≤ Survivor 容量,就能保证紧凑复制成功
实际效果:分配快、GC 停顿短、无整理开销
正因为内存始终以大块连续形式存在:
- 新对象分配只需移动一个指针(如 Eden 的 top 指针),是 O(1) 操作
- Minor GC 不需要像 CMS 或 G1 那样做碎片检查或空闲链表维护
- 即使频繁创建/销毁大量短期对象,Eden 区也能持续高效利用,不会因碎片导致提前触发 GC 或分配失败
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










