1. 分代收集算法的核心思想
分代收集算法基于一个重要的经验假设:
绝大多数对象都是"朝生夕死"的,只有少数对象会存活很久。
因此,JVM 将堆内存划分为不同的区域,并针对不同区域的对象生命周期特点,采用不同的垃圾回收策略,以提高效率、减少停顿时间。
2. 堆内存的分代结构
在 HotSpot JVM 中,堆通常分为:
- 新生代(Young Generation)
- Eden 区:新创建的对象首先分配在这里
- Survivor 区:两个 Survivor 区(S0、S1)用于在 Minor GC 时存放存活对象
- 老年代(Old Generation)
- 存放生命周期较长的对象
- 元空间(Metaspace)
- 存放类元数据(不在堆中)
3. 分代收集算法的原理
不同代使用不同的 GC 算法:
- 新生代 :对象存活率低 → 复制算法(Copying)
- 老年代 :对象存活率高 → 标记-整理(Mark-Compact) 或 标记-清除(Mark-Sweep)
这样做的好处:
- 新生代 GC(Minor GC):速度快,内存碎片少
- 老年代 GC(Major GC / Full GC):减少碎片,保证长期运行稳定
4. 分代收集算法的执行流程
4.1 新生代回收(Minor GC)
触发条件 :Eden 区满时触发
流程:
- 标记存活对象:从 GC Roots 出发,标记 Eden 和 Survivor 中的存活对象
- 复制存活对象 :
- 将存活对象从 Eden 和当前 Survivor 区复制到另一个 Survivor 区
- 对象的年龄(Age)+1
- 晋升到老年代 :
- 如果对象年龄超过阈值(默认 15),或 Survivor 区放不下,则晋升到老年代
- 清空 Eden 和原 Survivor 区:释放空间
4.2 老年代回收(Major GC / Full GC)
触发条件:
- 老年代空间不足
- System.gc() 调用
- 元空间溢出等特殊情况
流程:
- 标记存活对象:从 GC Roots 出发,标记老年代的存活对象
- 整理或清除 :
- 标记-整理:移动存活对象到一端,消除碎片
- 标记-清除:直接清除未标记对象(可能产生碎片)
- 释放空间:更新分配指针
4.3 整体分代收集流程示意
| 阶段 | 区域 | 算法 | 特点 |
|---|---|---|---|
| Minor GC | 新生代 | 复制算法 | 快速、无碎片 |
| Major GC | 老年代 | 标记-整理 / 标记-清除 | 存活率高、减少碎片 |
| Full GC | 新生代 + 老年代 + 元空间 | 综合使用 | 停顿时间长 |
5. 分代收集的优势
- 针对性强:不同代使用最适合的算法
- 性能高:新生代 GC 快速,减少整体停顿
- 碎片少:复制算法和整理算法减少内存碎片
✅ 总结: 分代收集算法的核心是:
- 新生代 → 复制算法
- 老年代 → 标记-整理 / 标记-清除
- 根据对象生命周期特点优化回收策略