Young GC 的触发时机
Young GC,也称为 Minor GC,其最根本、最直接的触发条件是:
当 JVM 尝试为一个新对象在 Eden 区分配内存,但发现 Eden 区剩余空间不足以容纳该对象时。
传统分代堆 (固定边界)
┌─────────────────┐
│ Eden │ ← 固定大小
├─────────────────┤
│ Survivor 0 │ ← 固定大小
├─────────────────┤
│ Survivor 1 │ ← 固定大小
├─────────────────┤
│ 老年代 │ ← 固定大小
└─────────────────┘
————————————————
-
初始状态:
- 我们有两个 Survivor 区:S0 和 S1。在任一时间点,其中一个被标记为 From Survivor(存有上一轮GC存活下来的对象),另一个是空的,被标记为 To Survivor。
- Eden 区是满的(因为触发了GC)。
-
GC 开始:
- GC 线程会同时扫描 Eden 区 和 From Survivor 区。
- 将这两个区域中所有存活的对象,一次性全部复制到 To Survivor 区。
- 在复制过程中,对象的年龄会增加1(来自From Survivor 区的老对象年龄+1,来自Eden的新对象年龄变为1
-
清空与交换:
- 在复制完成后,Eden 区 和 From Survivor 区 就被完全清空了(因为它们里面的存活对象都被搬走了,剩下的都是垃圾,直接忽略)。
- 然后,两个 Survivor 区的角色发生交换:原来的 To Survivor 现在变成了 From Survivor,准备迎接下一次GC。原来的 From Survivor(现在已是空的)则变成了 To Survivor。
触发 Young GC 后:
- 复制:
- Eden 中存活的对象 + S0 中存活的对象 → 全部复制到 S1。
- 在复制过程中,对象的年龄会增加1(来自S0的老对象年龄+1,来自Eden的新对象年龄变为1)。
为什么设计成两个 Survivor 区?
这种“From-To”的复制算法核心目的是解决内存碎片化问题。
- 如果只有一个 Survivor 区,经过几次GC后,存活对象在其中被来回搬动,会产生大量内存碎片。
- 而使用两个 Survivor 区,通过每次GC都将存活对象紧凑地复制到另一个空的 Survivor 区,可以确保 To Survivor 区在复制完成后总是紧凑无碎片的,这为后续的内存分配提供了极大的便利。
