## 为什么需要 JMH
- JVM 会进行 JIT 优化、内联与逃逸分析;直观写的微基准容易偏差。
- JMH 提供标准的 Runner、注解与统计方法,能较好避开测量伪影。
## 最小可运行示例
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(2)
@State(Scope.Thread)
public class HelloJMH {
@Benchmark
public int add() {
int a = 1, b = 2;
return a + b;
}
}
运行:
mvn -q -DskipTests package
java -jar target/benchmarks.jar
## Blackhole 与常见陷阱
- 不使用结果会被优化掉;用 `Blackhole` 消耗结果避免死代码消除。
import org.openjdk.jmh.infra.Blackhole;
@Benchmark
public void compute(Blackhole bh) {
int s = 0;
for (int i = 0; i < 1000; i++) s += i;
bh.consume(s);
}
## 参数化与状态
@State(Scope.Thread)
public class ParamState {
@Param({"16","64","256"})
int size;
int[] data;
@Setup(Level.Trial)
public void setup() {
data = new int[size];
for (int i = 0; i < size; i++) data[i] = i;
}
}
@Benchmark
public int sum(ParamState st) {
int s = 0;
for (int v : st.data) s += v;
return s;
}
## 预热、测量与 Fork
- 预热用于 JIT 暖机;测量阶段才统计结果。
- Fork 会在独立 JVM 中运行,降低 JVM 状态干扰;建议 `Fork >= 2`。
- 保持稳定环境:固定 CPU 频率与线程数;避免同时运行其他负载。
## 逃逸分析与对象分配
- 小对象可能被标量替换或栈上分配;在微基准中会影响结论。
record Pair(int a, int b) {}
@Benchmark
public int scalarReplace() {
Pair p = new Pair(1, 2);
return p.a() + p.b();
}
## 结果解读
- 关注误差范围(`±`)与单位,优先比较量级与趋势。
- 对关键参数做单因素变化(例如 `size` 或算法实现),避免混杂变量。
## 验证建议
- 与端到端压测结合:微基准仅反映局部,实现更改需在整体负载验证。
- 固定 JVM 参数(如堆大小与 GC),保证不同运行的可比性。
## 注意事项
- 关键词与主题一致:JMH、微基准、逃逸分析、预热与 Fork。
- 分类匹配:`软件/编程语言/Java`。
- 描述准确:强调可信测量与可复现方法。
- 技术参数已验证:JMH 注解与运行方式为常规用法;对象分配与逃逸分析为 JVM 通用行为。

发表评论 取消回复