一 工具简介
官方定义:JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targeting the JVM
JMH 主要是基于方法层面的基准测试,作为rd的我们使用它,可以评估自己代码的性能。
二 适用场景
1、技术插件选型:使用JMH测试具备相同功能的插件,从Throughput、Average Time等维度评估
2、热点方法优化:对热点方法进行优化,可使用JMH对比测试,不断迭代优化
三 操作手册
3.1 基础概念
Iteration
- iteration 是 JMH 进行测试的最小单位,包含一组 invocations。Invocation
- 一次 benchmark 方法调用。Operation
- benchmark 方法中,被测量操作的执行。如果被测试的操作在 benchmark 方法中循环执行,可以使用@OperationsPerInvocation
表明循环次数,使测试结果为单次 operation 的性能。Warmup
- 在实际进行 benchmark 前先进行预热。因为某个函数被调用多次之后,JIT 会对其进行编译,通过预热可以使测量结果更加接近真实情况。
3.2 常用注解
序号
|
注解
|
用法
|
说明
|
|
---|---|---|---|---|
1 |
@BenchmarkMode |
1、配置 Mode 选项,可用于类(接口/枚举)或者方法上,可被继承 2、这个注解的 value 是一个数组,可以把几种 Mode 集合在一起执行。 如@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}) 3、参数配置可以被runtime options覆盖 |
Throughput-吞吐量,每秒执行了多少次方法,单位是ops/time AverageTime-平均响应时间(耗时),单位是time/op SampleTime-对所有的操作时间采样时间 SingleShotTime-单次运行获取测量时间,一般用于测试冷起的耗时,建议把Warmup 次数设为 0(即无需预热) All-以上4中方式均执行一遍 |
|
2 |
@Benchmark |
加在方法上,标注该方法可以进行基准测试 | 标注的方法必须被public修饰 | |
3 |
@Warmup |
1、可以用在类(接口/枚举)或方法上,可被继承 2、配置预热需要的基本参数 3、参数配置可以被runtime options覆盖 |
为什么要预热?JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译为机器码,从而提高执行速度,为了让 benchmark 的结果更加接近真实情况就需要进行预热 iterations-预热迭代次数,默认-1 time-每次预热时间,默认-1 timeUnit-预热时间单位,默认second batchSize-每次批量执行基准方法的个数,默认-1 |
|
4 |
@Threads |
1、可以用在类(接口/枚举)或方法上,可被继承 2、参数配置可以被runtime options覆盖 |
每个测试进程中,用于测试的最大线程数 MAX-初始值-1,取值来源是Runtime.getRuntime().availableProcessors() |
|
5 |
@State |
1、用在类(接口/枚举)上,可被继承 2、定义state object在不同工作线程之间共享的范围 |
state object通常作为参数注入到benchmark方法上,也可以注入到@Setup 和@TearDown 里. Scope.Benchmark-相同类型的实例在所有的work线程之间共享 Scope.Group-同一组内的work线程共享相同类型的实例,每个线程组有自己的state object Scope.Thread-每个线程私有state object |
|
6 |
@Setup |
1、只能用在方法上,不可继承 2、在Benchmark标注的方法执行前运行,做数据初始化 |
该注解只能在@state使用的类(接口/枚举)里配合使用 Level.Trail-在每次基准运行前执行该方法 Level.Iteration-在基准每次循环前执行该方法 Level.Invocation-在每个基准方法执行前执行 |
|
7 |
@TearDown |
1、只能用在方法上,不可继承 2、在Benchmark标注的方法执行后运行 |
该注解只能在@state使用的类(接口/枚举)里配合使用 Level.Trail-在每次基准运行后执行该方法 Level.Iteration-在基准每次循环后执行该方法 Level.Invocation-在每个基准方法执行后执行 |
|
8 | @Timeout |
1、可以用在类(接口/枚举)或方法上,可被继承 加在方法上,仅对测试类的单一方法生效。 加在类上,对测试类内部所有@Benchmark方法生效 |
默认单位是seconds | |
9 | @Param |
1、只可以用在属性声明上,可以被继承 |
该注解用来标识在基准测试中可以配置化的参数,适合用来测试一个函数在不同的参数输入的情况下的性能 1、字段不能被final修饰且类上使用了@state修饰 2、可以用在string、枚举、基本数据类型及其包装类。取值已字符串形式给出 举例: @param("1","3","5") private Integer num; 3、当基准测试运行需要多个@Param时,JMH将计算运行中所有参数的外积 |
|
10 | @OutputTimeUnit |
1、可以用在类(接口/枚举)或方法上,可被继承 2、用在方法上仅对该方法生效;用在类上对类内所有方法生效 3、参数配置可以被runtime options覆盖 |
配置报告结果的默认时间单位 | |
11 | @OperationsPerInvocation |
todo 待补充 | ||
12 |
@GroupThreads |
1、只用在方法上,可以被继承 定义同一组内有多少个线程运行在被注释的方法上 |
默认值为1 | |
13 |
@Measurement |
1、可以用在类(接口/枚举)或方法上,可被继承 2、实际基测使用的参数 3、参数配置可以被runtime options覆盖 跟@Warmup类似 |
iterations-预热迭代次数,默认-1 time-每次预热时间,默认-1 timeUnit-预热时间单位,默认second batchSize-每次批量执行基准方法的个数,默认-1 |
|
14 |
3.3 idea 使用
3.4 使用注意点
四 引用
github地址:https://github.com/openjdk/jmh