JVM中对象如何从伊甸园区(Eden)进入幸存者区(Survivor)

JVM中对象如何从伊甸园区(Eden)进入幸存者区(Survivor)

JVM中对象如何从伊甸园区(Eden)进入幸存者区(Survivor)

在 JVM 的垃圾回收(GC)过程中,对象从 伊甸园区(Eden) 进入 幸存者区(Survivor) 的过程是 Minor GC(年轻代垃圾回收) 的核心机制。下面详细解释这一过程的步骤、条件和底层原理。

1. 对象分配流程(进入 Eden 区)

新对象优先分配在 Eden 区: 当程序通过 new 关键字创建对象时,JVM 会尝试在 Eden 区 分配内存。

如果 Eden 区空间不足,触发 Minor GC(年轻代垃圾回收)。如果 Minor GC 后仍不足,可能直接晋升到老年代(取决于 JVM 配置)。

2. Minor GC 触发时:对象从 Eden → Survivor

当 Eden 区满 时,JVM 会触发 Minor GC,存活的对象会被转移到 Survivor 区。具体步骤如下:

步骤 1:标记存活对象

可达性分析(GC Roots): JVM 从 GC Roots(如线程栈帧的局部变量、静态变量等)出发,标记所有 可达对象(存活对象)。

Eden 区中 被引用 的对象会被标记为存活。未被引用 的对象(垃圾)会被回收。

步骤 2:复制存活对象到 Survivor 区

存活对象从 Eden 复制到 Survivor(To 区):

所有 Eden 区存活的对象会被复制到 To Survivor(S0 或 S1)。对象年龄(Age) +1:每经历一次 Minor GC,对象的年龄增加 1(存储在对象头中)。Survivor 区采用“复制算法”:

每次 Minor GC 时,存活对象会被复制到 空的 Survivor 区(From 和 To 交替使用)。例如:

第一次 GC:Eden → S1(假设 S0 为空)。第二次 GC:Eden + S1 → S0(S1 变为 From,S0 变为 To)。

步骤 3:清空 Eden 区和 From Survivor

Eden 区和 From Survivor 被清空:

复制完成后,Eden 区和 当前的 From Survivor(存放上次存活对象的 Survivor)会被完全清空。From 和 To 角色互换:

原来的 To Survivor 变成下一轮的 From Survivor。原来的 From Survivor 变成下一轮的 To Survivor(等待接收新存活对象)。

3. 对象晋升老年代的条件

如果对象在 Survivor 区存活足够长时间,最终会晋升到 老年代(Old Generation)。晋升条件包括:

年龄阈值(MaxTenuringThreshold):

默认 -XX:MaxTenuringThreshold=15(经历 15 次 Minor GC 后晋升)。可通过 JVM 参数调整,例如 -XX:MaxTenuringThreshold=5。 动态年龄计算:

如果某一年龄(如 age=2)的对象总大小超过 Survivor 区的一半,则 ≥该年龄的对象直接晋升(避免 Survivor 区溢出)。 Survivor 区空间不足:

如果 Survivor 区无法容纳所有存活对象,部分对象会直接晋升到老年代(即使年龄未达标)。

4. 示例流程(Eden → Survivor → Old)

假设初始状态:

Eden 区:存放新对象 A, B, C。From Survivor(S0):空。To Survivor(S1):空。

第一次 Minor GC:

标记存活对象:假设 A, C 存活,B 被回收。复制到 To Survivor(S1):A, C 被复制到 S1,年龄 +1(A.age=1, C.age=1)。清空 Eden 和 From Survivor:Eden 和 S0 被清空。角色互换:

S1 变为 From Survivor(存放存活对象)。S0 变为 To Survivor(等待下次 GC)。

第二次 Minor GC:

Eden 区新分配对象 D, E, F。标记存活对象:假设 D, F 存活,E 被回收;S1 中的 A, C 仍然存活。复制到 To Survivor(S0):A, C, D, F 复制到 S0,年龄 +1(A.age=2, C.age=2, D.age=1, F.age=1)。清空 Eden 和 From Survivor(S1)。角色互换:

S0 变为 From Survivor。S1 变为 To Survivor。

后续晋升:

如果 A.age=15(默认阈值),则 A 会在下次 GC 时晋升到 老年代。

5. 关键 JVM 参数

参数作用建议值-XX:SurvivorRatioEden 区与单个 Survivor 区的比例(默认 8)。-XX:SurvivorRatio=6(增大 Survivor)。-XX:MaxTenuringThreshold对象晋升老年代的年龄阈值(默认 15)。-XX:MaxTenuringThreshold=5(减少年轻代停留)。-XX:+PrintGCDetails打印 GC 详情(观察 Eden/Survivor 变化)。调试时启用。-XX:PretenureSizeThreshold大对象直接进入老年代的阈值(默认 0)。-XX:PretenureSizeThreshold=1M(避免大对象占用 Eden)。

6. 常见问题

Q1:为什么 Survivor 区要分为 From 和 To?

复制算法(Copying Algorithm)要求:

每次 Minor GC 时,存活对象被复制到 空的 Survivor 区(To),然后清空原来的 Survivor 区(From),避免内存碎片。From 和 To 交替使用,确保始终有一个 Survivor 区是空的。

Q2:如果 Survivor 区空间不足怎么办?

直接晋升老年代:

如果 Survivor 区无法容纳所有存活对象,部分对象会 直接进入老年代(即使年龄未达标)。可通过 -XX:SurvivorRatio 调整比例,或增大年轻代总大小(-Xmn)。

Q3:如何监控对象年龄分布?

使用 jstat 或 -XX:+PrintTenuringDistribution:jstat -gc 1000 # 每 1 秒输出 GC 数据

或-XX:+PrintTenuringDistribution # 打印对象年龄分布

总结

Eden → Survivor:Minor GC 时,存活对象从 Eden 复制到 To Survivor,年龄 +1。Survivor 区交替使用:From 和 To 在每次 GC 后交换角色。晋升老年代:年龄 ≥ MaxTenuringThreshold 或 Survivor 空间不足时晋升。调优关键:通过 SurvivorRatio 和 MaxTenuringThreshold 优化对象生命周期。

通过 jvisualvm 或 GC 日志 可以直观观察对象在 Eden/Survivor 区的流动情况。

相关推荐

阴阳师呱太作用解析及集齐全攻略_呱太们的实用价值与收集指南
如何解决创建JSP文件时出现的报错问题?
15种促进新陈代谢和燃烧脂肪的食物
有趣的公益手游大全 2025耐玩的公益游戏合集
询的意思,询的解释,询的拼音,询的部首,询的笔顺
手把手教你为老电脑安装 Windows 7 - 杨奇的博客