直接用 System.nanoTime() 测并发性能会失真,因受JIT未优化、GC停顿、线程调度抖动等干扰,无法反映稳定吞吐与多线程扩展性瓶颈,必须通过预热、分离测量轮次、控制JVM参数及合理配置@State和@Threads来保障准确性。

为什么直接用 System.nanoTime() 测并发性能会失真
因为 JVM 的即时编译(JIT)、代码预热、GC 干扰、线程调度抖动,会让单次或简单循环测量完全不可靠。比如你看到某个 ConcurrentHashMap.put() 耗时 50ns,实际可能是 JIT 还没优化完,或者被 GC STW 拦腰截断。
真实并发场景下,你要测的是「稳定吞吐下的平均延迟」和「多线程竞争时的扩展性瓶颈」,不是单线程冷启动那一瞬间。
- 必须做预热(warmup):让 JIT 编译完成、类加载完毕、分支预测稳定
- 必须分离测量轮次(measurement iterations)与预热轮次,不能混在一起统计
- 必须控制线程数变量:用
@Fork(jvmArgsAppend = {"-Xmx2g"})避免 GC 扰动,而不是默认 JVM 参数 - 避免在测试方法里写
Thread.sleep()或System.out.println()—— 它们会污染同步语义和时间戳精度
@State 和 @Threads 怎么配才不踩共享状态坑
很多人把共享对象(比如一个 ConcurrentLinkedQueue)声明在 @State(Scope.Benchmark) 类里,却忘了每个 fork 进程里只会有一个实例 —— 这没问题;但一旦开了多个线程(@Threads(4)),它们就真正在争抢这个实例。问题在于:你得确认这正是你想测的场景。
比如测锁竞争,就要用 Scope.Benchmark;但如果你本意是测单线程吞吐,却误加了 @Threads(4),结果其实是 4 个线程在抢同一把锁,数据全乱。
立即学习“Java免费学习笔记(深入)”;
-
@State(Scope.Benchmark):所有线程共享同一个实例(适合测并发容器/锁) -
@State(Scope.Thread):每个线程独享一份(适合测无共享逻辑,如纯计算) -
@Threads(8)不等于“开 8 个线程跑 8 次”,而是 8 个线程持续并发执行,吞吐量单位是 ops/s,不是单次耗时 - 别在
@Setup里初始化可变静态字段——fork 进程之间不共享静态域,会导致状态错乱
吞吐量(Mode.Throughput)结果怎么看才不误解
JMH 默认输出的 ops/s 是「每秒完成的操作次数」,但它背后是按 measurement iteration 的总耗时反推出来的。比如 10 秒内 4 个线程共完成 400 万次 offer(),结果就是 400_0000 / 10 = 400000 ops/s —— 注意这是**整体吞吐**,不是单线程吞吐。
容易误读的是:看到 @Threads(8) 下吞吐只比 @Threads(4) 高 1.2 倍,就以为“扩展性差”。其实要看是否达到硬件瓶颈(比如 CPU 已 100%,或缓存行伪共享开始起效)。
- 对比不同线程数时,固定 fork 数(
@Fork(1))和预热轮次(@Warmup(iterations = 5)),否则 warmup 不充分会导致低线程数结果虚高 - 用
ResultFormat.RAW导出 JSON,再用jmh-result-analyzer看吞吐随线程数变化的曲线,比看单点数值有用得多 - 注意单位:JMH 输出的
±是置信区间(99.9%),不是标准差;如果误差 >15%,说明测量不稳定,要检查 GC 或系统负载
配置 @Fork 和 JVM 参数时最常漏掉的三件事
很多人只写 @Fork(3),以为就是跑三次取平均,却忽略了每次 fork 是独立 JVM 进程 —— 这本来是 JMH 抗干扰的核心设计,但若不显式约束内存和 GC,三次结果可能分别被 G1、ZGC、甚至 CMS 干扰。
更隐蔽的问题是:没禁用 TieredStopAtLevel,导致 JIT 编译层级不一致;或没关 -XX:+UseStringDeduplication,让字符串操作基准飘忽不定。
- 强制指定编译层级:
-XX:TieredStopAtLevel=1(C1 only)或=4(C2 full),避免预热期和测量期编译策略切换 - 禁用非确定性优化:
-XX:-UseBiasedLocking -XX:-UseCounterDecay,尤其测锁性能时 - 堆大小必须显式设(如
-Xmx4g -Xms4g),否则默认堆可能触发 GC,且不同 fork 进程堆行为不一致
复杂点不在写法,而在于你得清楚自己到底在测 JVM 行为、算法逻辑,还是硬件调度。测错一层,结论就偏一整个栈。











