survivor区大小需适中:太小导致对象频繁晋升老年代、触发分配担保失败;太大则压缩eden区、增加minor gc频率。合理值应使gc后survivor占用率稳定在60%~80%,晋升量低且minor gc间隔拉长。

复制算法的内存利用率,核心在于Survivor区不能太小也不能太大——太小会导致对象频繁晋升老年代,增大Full GC压力;太大则浪费新生代空间,降低Eden区容量,反而提前触发Minor GC。关键不是“最大化利用率”,而是让Survivor区刚好能容纳住那些真正能活过几次GC的幸存对象。
Survivor区太小的典型表现
当Survivor空间不足时,Minor GC过程中存活对象无法全部复制进去,JVM会直接将部分对象“担保分配”到老年代,这种现象叫“分配担保失败”或“survivor overflow”。日志中常见提示:Desired survivor size … but actual … is smaller,或出现大量tenured promotion记录。这往往不是因为对象生命周期长,而是Survivor根本装不下。
Survivor区太大的隐性代价
Survivor占比过高(比如SurvivorRatio=3),意味着Eden区被压缩。例如新生代128MB、SurvivorRatio=3时,Eden仅占60%,约76.8MB;而SurvivorRatio=8时,Eden达102.4MB。Eden越小,对象分配越快填满,Minor GC频率自然上升——哪怕每次GC只回收95%的对象,高频GC本身就会增加CPU开销和应用停顿次数。
判断当前Survivor是否合适的方法
不靠猜测,靠GC日志中的两项关键指标:
-
GC后Survivor占用率:用
-XX:+PrintGCDetails开启日志,观察每次Minor GC后S0或S1的使用百分比。长期稳定在60%~80%之间,说明大小较合理;若常低于30%,说明Survivor偏大;若多次接近100%甚至溢出,则明显偏小。 -
对象晋升量(Promotion):日志中
PSYoungGen段末尾的->后数字,是本次GC后进入老年代的字节数。如果单次晋升量持续超过几MB,或晋升速率明显高于业务请求节奏,就要检查Survivor是否撑不住了。
按场景调整SurvivorRatio的实用建议
没有万能值,但有可落地的参照:
-
短生命周期为主的服务(如HTTP API网关):90%以上对象活不过一次GC。可设
-XX:SurvivorRatio=12~15,Eden占比超88%,减少GC频次,Survivor留够基础缓冲即可。 -
含较多中间对象的业务(如报表生成、批量转换):对象常需2~4次GC才死亡。建议
-XX:SurvivorRatio=4~6,每个Survivor占14%~20%,确保多轮复制不溢出。 - 已知对象平均年龄为3~5(通过-XX:+PrintTenuringDistribution确认):说明Survivor需至少承载3轮复制。此时SurvivorRatio不宜高于6,否则单区容量可能不足以支撑对象完成年龄积累。
关闭自适应策略更利于观察效果:-XX:-UseAdaptiveSizePolicy,避免JVM动态调整覆盖你的设定。调优后关注Minor GC间隔是否拉长、晋升量是否下降、应用响应P99是否更平稳——这些才是真正的利用率优化信号。











