调优目标与参数设定
用途:给一个服务定 GC 与内存参数,并知道每个参数在优化什么
先定目标再调参
「先定目标再调参」小节链接三个必须写下来的数字:
- 延迟目标:应用 P99/P999 RT 上限,以及可接受的单次最大停顿。
- 吞吐目标:GC 时间占比上限(在线服务常取 < 3-5%)。
- 容量目标:单机 QPS、实例数、容器内存限制。
没有目标就没有“调好了”的判断依据,只能瞎试参数。
基础参数集
「基础参数集」小节链接| 参数 | 作用 | 设定建议 |
|---|---|---|
-Xms / -Xmx |
堆初值/上限 | 在线服务常设相同,避免运行期扩缩抖动;容器内留出栈/元空间/堆外余量 |
-XX:MaxMetaspaceSize |
元空间上限 | 显式设置,防类加载泄漏吃满内存 |
-XX:MaxDirectMemorySize |
堆外上限 | 使用 NIO/Netty 时必须显式限 |
-Xss |
线程栈大小 | 线程多时下调(如 512k),但递归深的应用慎用 |
-XX:MaxGCPauseMillis |
G1 停顿目标 | 200ms 起步,按应用 P99 反推,不要盲目调小 |
-XX:InitiatingHeapOccupancyPercent |
并发标记触发阈值 | 默认 45;晋升波动大时下调 |
-XX:+UseStringDeduplication |
字符串去重(G1) | 字符串密集且内存吃紧时评估 |
-Xlog:gc* |
GC 日志 | 生产必开,输出到文件并轮转 |
-XX:+HeapDumpOnOutOfMemoryError |
OOM 时 dump | 生产开启,注意磁盘与 dump 体积 |
调优顺序(性价比从高到低)
「调优顺序(性价比从高到低)」小节链接- 修代码:减少分配速率(对象复用、避免装箱与临时集合、字符串拼接、日志格式化)、修内存泄漏、避免大对象。
- 给对内存:堆大小与容器限制匹配,避免过小的 Young 区或过大的堆导致单次回收耗时。
- 选对收集器:按延迟/吞吐目标选 G1 / Parallel / ZGC。
- 微调参数:停顿目标、IHOP、Region 大小、并行线程数。
- 架构层:拆分实例、隔离大任务、把批处理与在线服务分开部署。
经验判断:绝大多数“GC 调优”的收益来自第 1 步,参数只是把系统能力对齐到代码的真实分配行为。
验证方法
「验证方法」小节链接- 压测固定流量,对比修改前后的:GC 频率、单次停顿 P99、总 GC 时间占比、应用 P99 RT、晋升速率。
- 至少覆盖 30 分钟真实流量曲线(含峰值与低谷),避免只看稳态。
- 保留 GC 日志与火焰图,做前后对照,而不是“感觉变快了”。
常见误区
「常见误区」小节链接| 误区 | 修正 |
|---|---|
| 网上抄一套“最佳参数” | 参数必须对应自身分配行为与目标 |
| 堆越大越好 | 单次回收成本与内存占用同步上升,容器 OOMKill 风险增加 |
| 只看 Young GC 次数 | 关注总停顿占比与尾停顿 |
| 忽略分配速率 | 停顿问题的根因常在分配,不在回收 |
| 生产直接改参数不灰度 | 逐实例灰度并保留回滚 |
延伸阅读
「延伸阅读」小节链接- OpenJDK HotSpot VM Options 与 GC Tuning 指南
- 《Java 性能权威指南》(Scott Oaks)
- Nitsan Wakart 关于分配速率与 GC 的公开文章