Java 性能测试怎么做:JMH 微基准和结果解读

2次阅读
没有评论

Java 方法级性能测试不能简单用 System.currentTimeMillis() 包一层循环。JVM 有预热、JIT 编译、逃逸分析、死代码消除、GC 和线程调度,普通测试很容易测到错误结论。JMH 就是为微基准测试准备的工具。

JMH 适合测小范围热点

JMH 适合在已经定位到热点方法后,比较不同实现的成本。例如字符串拼接、序列化方式、集合操作、算法实现、不同输入规模下的耗时变化。

它不适合替代完整链路压测。接口响应时间、数据库访问、网络调用、缓存命中率这些问题,仍然需要压测、APM、日志和 profiler 一起看。

微基准回答的是“小片代码怎么表现”,不是“整个系统能扛多少流量”。

预热和测量要分开

JVM 刚启动时,代码可能还在解释执行,热点方法还没有被 JIT 编译。直接测第一次运行结果,通常不稳定。

JMH 用 @Warmup 设置预热轮次,用 @Measurement 设置正式测量轮次。预热让运行状态更接近长期服务进程,正式测量再输出可比较数据。

@Fork 可以让测试在新的 JVM 进程里跑,减少当前进程状态对结果的影响。

防止代码被优化掉

如果测试方法计算了一个结果但没有使用,JIT 可能认为这段代码没有意义,直接优化掉。这样测出来的结果会非常漂亮,但没有任何业务价值。

常见处理方式是返回结果,或者使用 JMH 提供的 Blackhole 消费结果。不要在 benchmark 里用空方法假装消费,也不要把日志打印放进核心测量路径。

测试模式要和问题匹配

JMH 常用模式包括:

  • Throughput:吞吐量,单位时间能执行多少次。
  • AverageTime:平均耗时。
  • SampleTime:采样耗时分布。
  • SingleShotTime:单次执行时间。

如果比较两个方法每秒能处理多少次,用吞吐量;如果关心单次延迟,用平均耗时或采样耗时。模式不同,分数不能直接横向比较。

并行不一定更快

并行求和、并行 Stream 或多线程算法,都有任务拆分、线程调度和结果合并成本。

输入规模小时,串行可能更快;输入规模足够大、计算足够重、共享状态足够少时,并行才可能体现优势。JMH 的 @Param 可以用不同输入规模跑一组结果,避免只拿一个数字下结论。

结果要看误差范围

JMH 输出里不要只看 Score,还要看 ErrorUnits

如果两个实现分数接近,但误差范围重叠很大,就不能轻易说谁更快。应该增加测量轮次、隔离机器负载、减少外部干扰,或者重新设计 benchmark。

微基准结论最好写清楚测试环境、输入规模、JDK 版本和测试模式,否则以后很难复现。

实用结论

JMH 能帮你把小范围性能判断从“感觉”变成“可测量”。它解决了 JVM 预热、JIT 优化、死代码消除、fork 隔离和结果统计这些细节。

但它不是万能压测工具。正确姿势是:先用线上指标或 profiler 找热点,再用 JMH 比较局部实现,最后回到真实链路验证整体收益。

正文完
 0
bdspAdmin
版权声明:本站原创文章,由 bdspAdmin 于2026-07-05发表,共计1170字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)