Java 性能方法论
Java 性能工作的正确顺序:定义指标、复现、剖析、改动、回归,避免过早优化。
#concept
#growing
#dev
#java
[!info] related notes
Java 性能方法论
这个概念为什么出现
“感觉慢”不能指导改代码。Java 有 JIT、GC、预热,错误基准会指向错误热点。方法论比某个收集器参数更重要。
[!abstract] 一句话理解 先定义用户指标与 SLO,再稳定复现,用剖析找热点,小步改动并回归;禁止一次
System.nanoTime包装的伪基准下结论。
最小可观察示例
场景:对比两种拼接在错误基准下的误导(教学)
public class BadBenchmark {
static String plus() {
String s = "";
for (int i = 0; i < 1000; i++) s += i;
return s;
}
static String builder() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.append(i);
return sb.toString();
}
public static void main(String[] args) {
long t1 = System.nanoTime();
plus();
long t2 = System.nanoTime();
builder();
long t3 = System.nanoTime();
System.out.println("plusNs=" + (t2 - t1));
System.out.println("builderNs=" + (t3 - t2));
System.out.println("note=not-a-real-benchmark");
}
}
javac BadBenchmark.java && java BadBenchmark
期望:打印两段耗时,并明确这不是科学基准。无预热和迭代统计、固定执行顺序,以及未消费返回值造成的死代码消除风险,都可能影响结果;不能把本例数字作为性能结论。
结合场景再看三个关注点
- 测稳态性能需考虑预热、多轮与独立 fork;若目标是冷启动,预热会掩盖目标成本,应单独设计实验。
- JMH 才是微基准常用框架。
- 系统级指标(延迟分位、吞吐、错误率)优先于微基准。
核心概念与准确模型
1. 工作流
指标 → 复现 → 剖析(JFR/采样)→ 假设 → 改动 → 回归
2. 常见热点
分配过多、锁竞争、IO、错误缓存、正则、日志
3. 工具
JFR/async-profiler 辅助定位运行时热点,JMH 用于受控微基准,指标系统观测真实负载。JMH 应消费返回值或使用 Blackhole,设置合理 warmup、measurement、fork;使用框架本身不保证实验代表业务负载。
边界情况与反直觉行为
- 开发机与生产硬件差异。
- GC 停顿表现为“接口毛刺”。
- 不能仅凭“提升 2%”判断价值:先确认差异高于噪声,再按总成本、SLO 收益与维护代价权衡。
常见误区
[!warning] 常见误区:先改代码再想指标 没有基线就没有优化。
工程实践
- SLO/SLA 写清。
- 负载测试贴近真实。
- 每次只改一类变量。
- 把剖析火焰图留档。
本节总结
- 方法论 > 玄学参数
- 测量与回归
- 微基准慎用
自测题
- 为什么需要预热?
- 延迟看平均值够吗?
参考答案
- JIT 与缓存升温会改变稳态性能。
- 不够;应看分位数(P95/P99)与错误率。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| JMH | 工具 | 微基准 |
| JDK Flight Recorder | 官方 | 剖析 |