性能工程¶
面试追问地图¶
| 主问题 | 必讲关键点 | 下一层追问 |
|---|---|---|
| 目标 | SLO、负载、基线、瓶颈 | 吞吐和延迟如何权衡 |
| JMH | 预热、fork、DCE、常量折叠 | Blackhole、状态作用域 |
| 压测 | 开放/封闭模型、数据分布、协调遗漏 | P99、容量拐点 |
| 剖析 | 采样、on/off-CPU、调用栈 | JFR 与 async-profiler |
| 验证 | 单变量、对照、置信度、资源账单 | 瓶颈是否转移 |
一、目标与基线¶
性能优化前要定义什么?¶
先定义业务负载和目标:请求比例、数据规模、并发模型、吞吐、P95/P99、错误率和资源成本。平均延迟正常不能证明用户体验正常,长尾请求会占住线程和连接并放大排队。
优化从基线开始:固定硬件、JDK、参数、数据集和依赖版本,记录 CPU、内存、GC、磁盘、网络和下游指标。没有基线的“快了 30%”无法复现,也不知道代价是否转移到别处。
二、微基准与服务压测¶
Java 微基准为什么要用 JMH?¶
JVM 有分层编译、方法内联、常量折叠和死代码消除,直接在循环前后读取时间很容易测到被优化掉的代码。JMH 提供预热、测量迭代、独立 fork、状态隔离和结果消费机制。
- 返回结果或使用
Blackhole,防止死代码消除。 - 输入不能都是编译期常量,否则可能测到常量折叠结果。
- 用多个 fork 隔离 JVM 状态,报告方差,不只挑最好一次。
- 微基准只证明局部代码路径,不代表应用吞吐;锁、缓存和分配行为要按真实并发度建模。
服务压测模型怎样设计?¶
封闭模型由固定并发用户“完成后再发下一次”,系统变慢时发压自然下降,可能掩盖过载;开放模型按计划到达率发请求,更接近外部流量。工具若只在上一次响应后记录下一次预期发送,会产生协调遗漏,低估排队和长尾。
压测要逐步升流量,找到延迟陡升、错误增加或资源饱和的容量拐点;数据热度、读写比例、缓存命中率和第三方限额应接近生产。压测环境不能承受真实规模时,应明确外推假设。
三、剖析与验证¶
火焰图怎样定位 CPU 热点?¶
采样分析器定期抓取线程栈,火焰图横向宽度表示样本占比,不表示时间顺序。先看宽平台定位热点方法,再结合调用来源、分配和锁事件判断根因。
CPU 火焰图看运行热点;off-CPU 分析关注锁等待、I/O 和 sleep。JFR 适合持续低开销记录 JVM/GC/锁/分配事件,async-profiler 适合按需采样 CPU、分配和锁;两者与指标、日志共同形成证据链。
怎样证明性能优化有效?¶
一次只改变一个主要变量,在相同负载下重复对照,比较吞吐、分位延迟、错误率和资源消耗。优化后继续升压确认新瓶颈,并做正确性回归。减少 20% CPU 却让 P99、内存或下游 QPS 恶化,不是完整的优化结论。