堆内存划分是依据应用对象生命周期特征动态调优的策略性配置,新生代与老年代协同设计:短命对象多则强化新生代容量与survivor缓冲能力,避免因survivor过小导致提前晋升;需结合gc日志中s0/s1使用率>90%、promoted量高、full gc频发等信号,优先调大-xmn并调整-xx:survivorratio,同时区分gc器(如parallel gc有效、g1无效),以日志验证调优效果。

堆内存划分不是固定模板,而是要根据应用对象生命周期特征动态调整的策略性配置。新生代和老年代不是简单按比例切分,而是围绕“对象存活时间分布”来协同设计的——短命对象多就强化新生代容量与结构,长命对象多则需保障老年代稳定性和晋升合理性。
新生代内部结构必须平衡:Eden不能挤占Survivor生存空间
默认 -XX:SurvivorRatio=8(即 Eden:S0:S1 = 8:1:1)在小新生代下极易失效。例如 -Xmn512m 时,每个 Survivor 仅约 25MB,一次 Minor GC 后若存活对象超 20MB,Survivor 就满,直接触发“担保失败”,大量对象被迫提前晋升到老年代。
- 观察指标:加 -XX:+PrintGCDetails,重点关注每次 Minor GC 后 S0 或 S1 的使用率是否长期 >90%
- 调优方向:优先调大 -Xmn(如升至 1g 或 1.5g),再配合 -XX:SurvivorRatio=6(S0/S1 各占新生代 ≈14%)提升缓冲能力
- 避免误区:只调 -XX:NewRatio 改老年代占比,却不检查新生代内部是否失衡,问题照旧
老年代不是“垃圾桶”,而是需要主动防护的稳定区
老年代增长快≠要拼命扩容,更可能是新生代“守不住”——对象没活过一轮 GC 就被顶进来了。真正健康的晋升应满足两个条件:一是对象确经多次 Minor GC 仍存活;二是晋升动作可控、可预期。
- 关键参数:-XX:MaxTenuringThreshold 控制晋升年龄阈值(默认 15),但实际晋升常由 -XX:TargetSurvivorRatio(默认 50%)动态触发:当某年龄批次对象总和超过 Survivor 一半,该年龄及以上对象全部晋升
- 大对象直入老年代:-XX:PretenureSizeThreshold 可设为略大于常见大缓存对象(如 1MB 字节数组),避免其反复复制消耗 Survivor 空间
- 监控信号:GC 日志中 Promoted 字段持续偏高 + Full GC 频次上升,大概率是新生代结构或阈值不合理所致
不同 GC 器对同一参数响应差异极大
Parallel GC 和 G1 对 -XX:SurvivorRatio 的理解和使用方式完全不同。Parallel GC 严格按比例分配固定大小的 Survivor 区;而 G1 已不再使用固定 SurvivorRatio,改用动态可变的 Region 分配机制,此时硬设该参数无效甚至被忽略。
- 确认当前 GC 器:启动时加 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps,日志首行会标明 UseParallelGC / UseG1GC 等
- G1 场景下:关注 -XX:MaxGCPauseMillis 和 -XX:G1HeapRegionSize,Survivor 相关调优应转向 -XX:G1NewSizePercent 和 -XX:G1MaxNewSizePercent
- 切忌套用“万能参数”:一套在 Parallel 下有效的 -Xmn/-XX:SurvivorRatio 组合,在 G1 下可能完全不生效,甚至引发反效果
调优闭环:从日志出发,以行为验证
参数调完不算结束,必须回归 GC 日志看三项核心变化:Minor GC 耗时是否缩短、Promoted 量是否下降、Full GC 是否延后或消失。没有日志佐证的配置都是假设。
- 必备日志参数:-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M
- 重点比对字段:GC pause(停顿时间)、Eden used(回收前使用率)、Survivor used(S0/S1 使用率)、Promoted(晋升量)、Old Gen used(老年代增长趋势)
- 验证周期:单次调整后至少观察 2–3 小时真实业务流量下的 GC 行为,避免仅靠压测短时数据下结论











