你们线上选的是什么垃圾收集器?为什么这么选,业务最关注哪些 GC 指标?
这个问题我会先说项目结论:从对象存活判定、分代回收到 G1 和 ZGC 的选择与排查
我会先交代项目背景和选型结论。JVM 主流实现通过可达性分析判断对象是否存活。从 GC Roots 出发沿引用关系遍历,不可达对象才具备被回收的条件。GC Roots 常见来源包括线程栈中的引用、静态字段引用、JNI 引用和 JVM 内部引用。 引用计数无法可靠处理循环引用,因此不是 HotSpot 判断对象存活的主要算法。
具体落地时,我会沿着实际调用链来讲。沿着「对象持续分配 → 可达性标记 → 存活对象复制或整理 → 回收不可达空间 → 调整代际与并发周期」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 对象持续分配 分配速率、对象大小和引用关系决定 GC 压力,吞吐问题常先从分配端产生。 可达性标记 GC Roots 沿引用图标记存活对象,引用类型、类加载器和线程局部变量都会影响可达性。 -Xlog:gc:停顿、阶段与堆区域。 JFR ObjectAllocationSample:对象分配热点。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。
参数和容量不能靠默认值,我会结合业务量来定。以真实对象存活率跑 30 分钟稳态和 5 倍峰值;对比 GC 前后占用、分配、晋升和业务 P99。
效果要用数据证明,线上问题也要能闭环。固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「GC 后堆」为主基线,记录值应满足「稳态水平线」;同时保存 分配速率、停顿 P95/P99,使后续变化能够回到同一时间轴比较。 监控只显示平均响应正常,GC 日志却表明每两分钟出现 Mixed GC,原因是缓存批量刷新制造大量中等寿命对象并集中晋升。拆散刷新批次、减少临时对象后,比先改收集器参数更直接。 只调停顿目标不降低分配速率:分配速率:先降低分配热点。 堆设得过满导致并发周期来不及:停顿 P95/P99:为并发 GC 留 CPU 余量。
最后我会主动说明这个方案不适合什么场景。方案:更适合的场景:主要收益:代价与边界。 G1:中大堆与通用暂停目标:Region 化、成熟通用:极低停顿目标下并发余量有限。 ZGC:大堆与亚毫秒级停顿诉求:并发移动、停顿短:需要较新 JDK 与足够 CPU/内存余量。 Parallel GC:吞吐优先的离线计算:实现简单、吞吐高:停顿随堆和存活对象增长。 选型至少带上 堆与非堆容量、对象分配速率、存活率和停顿目标,并用上面的量化基线验证;