新生代采用复制算法,由eden和两个survivor区(s0、s1)动态协作:新对象仅分配至eden,其满时触发minor gc,仅扫描eden与from区,存活对象复制至to区并增龄,随后清空eden与from区,s0/s1角色互换;8:1:1比例基于统计优化,survivor不足时启动分配担保,对象年龄达阈值或动态判定后晋升老年代,大对象直接入老年代。

新生代用复制算法,核心就是靠 Eden 和两个 Survivor 区(S0、S1)来回配合,不是简单“复制一次就完事”,而是一套动态联动机制——对象在哪分配、谁来收、往哪搬、什么时候走,全由这三块区域协同决定。
Eden 是对象的“主入口”,也是 Minor GC 的触发开关
新对象默认只进 Eden,不直接去 Survivor。Eden 空间大(默认占新生代 80%),能撑住大量短命对象集中创建。一旦 Eden 满了,立刻触发 Minor GC——这时不会动老年代,也不扫描整个新生代,只聚焦 Eden + 当前“From”Survivor 区。
- GC Roots 可达的对象被标记为存活
- 所有存活对象统一复制到空的“To”Survivor 区(比如 S1)
- 复制同时,对象年龄 +1(Eden 里活下来的算 1 岁,From 区里活下来的年龄累加)
- 复制完成后,Eden 和 From 区直接清空,不留碎片
两个 Survivor 区轮流当“搬运工”,避免单区堆积
如果只有一个 Survivor 区,第二次 GC 时它自己就存着上一轮的存活对象,再往里复制新对象,就得边清理边搬,效率低还容易溢出。双 Survivor 的设计让“搬运”和“暂存”彻底分离:
- 每次 GC 前,总有一个 Survivor 是空的(To 区),专等接收复制对象
- 另一个 Survivor(From 区)连同 Eden 一起被扫描、清空
- GC 结束后,S0 和 S1 角色自动交换:原来的 To 变成下轮的 From,原来的 From 变成下轮的 To
- 这样既保证每次都有干净目标区,又让对象年龄自然累积,便于后续晋升判断
内存比例不是硬约束,而是对“朝生夕死”的工程适配
8:1:1(Eden:S0:S1)不是理论推导出来的黄金比例,而是基于大量实际应用统计得出的平衡点:
- 约 80% 的对象在第一次 GC 就死亡,Eden 占 80% 刚好够用,浪费少
- 剩下约 20% 存活对象中,绝大多数撑不过两次 GC,10% 的 Survivor 空间通常绰绰有余
- 真遇到 Survivor 不够用(比如一批对象集体变长命),JVM 会启动“分配担保”机制,把多出的部分直接挪到老年代,避免 Minor GC 失败
- 可通过 -XX:SurvivorRatio=6 等参数调整比例,但改之前得看实际 GC 日志里 Survivor 空间使用率和晋升量
对象年龄不是摆设,是晋升老年代的关键闸门
每个对象头里藏着一个年龄计数器,Minor GC 每成功活过一轮,年龄 +1。这个数字直接决定它还能在 Survivor 区待几轮:
- 默认阈值是 15(-XX:MaxTenuringThreshold=15),达到就进老年代
- 但 JVM 实际更灵活:如果某次 GC 后,To 区放不下所有存活对象,会提前把年龄较大的一批直接送走,叫“动态年龄判定”
- 大对象(如大数组)压根不走这套流程,直接分配进老年代,省去反复复制的开销










