-
推荐在maven项目中使用jmh;
-
引入pom依赖:
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.18</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.18</version> </dependency>
-
在需要基准测试类中声明
main
方法,如:public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(yourClassName.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); }
-
在需要测试的方法上加上
@Benchmark
注解,如:@Benchmark public void wellHelloThere() { System.out.println("Hello World."); }
-
可以参考下这个JMHSample_01_HelloWorld。
有三种方式:
-
通过命令行:
mvn clean install java -jar target/benchmarks.jar yourClassName
-
将JMH与测试代码一起打包,然后使用以下命令:
java -jar target/benchmarks.jar -h
-
通过Java API,如上述的程序实例。
-
用途:指定需要作基准测试的方法;
-
作用域:方法;
-
约束不仅限于:
- 方法为
public
; - 方法中的参数必须带有
@State
注解; - 如果
@State
注解的是内部静态类,则该方法需要加synchronized
。
- 方法为
-
用途:指定基准测试方法的运行模式;
-
作用域:方法;
-
运行模式类型:
-
Mode.Throughput
计算时间单位内操作次数,该模式是基于时间的,直到迭代时间过期才会停止运行。
-
Mode.AverageTime
计算单次操作的平均时间,该模式是基于时间的,直到迭代时间过期才会停止运行。
-
Mode.SampleTime
计算每次操作的时间(包含百分比),该模式是基于时间的,直到迭代时间过期才会停止运行。
-
Mode.SingleShotTime
方法仅运行一次(不会执行预热动作)。
-
除上述以外,也可以以多种组合模式运行测试方法。
-
- 用途:指定测试方法输出的时间单位;
- 作用域:方法或类。
-
用途:定义给定类对象的可用范围;
-
作用域:公共类或静态内部类;
-
类作为测试方法入参的条件:
- 有无参构造函数;
- 是公共类或静态内部类;
- 该类必须使用@State注解。
-
Scope类型:
-
Scope.Thread
默认状态。实例将分配给运行测试方法的每个线程,不会共享。
-
Scope.Benchmark
运行相同测试的所有线程将共享实例,可用来测试状态对象的多线程性能。
-
Scope.Group
实例分配给每个线程组,在同一线程组的线程会共享该变量。
-
-
实例可见这里。
- 用途:定义状态对象的生命周期方法;
- 作用域:具有
@State
注解的类中的方法上; - 实例可见JMHSample_05_StateFixtures。
-
用途:用于控制何时执行
@Setup
和@TearDown
方法; -
作用域:方法;
-
执行级别类型:
-
Level.Trial
默认级别,在每次启动测试类前/后执行。
-
Level.Iteration
在每次迭代前/后执行。
-
Level.Invocation
在每次调用测试方法前/后执行。
-
-
实例可见JMHSample_06_FixtureLevel,JMHSample_07_FixtureLevelInvocation。
- 用途:用于指明测试方法中的循环次数,便于测试单次循环操作的性能;
- 作用域:方法或类;
- 实例可见JMHSample_11_Loops。
- 用途:设置测试方法的运行Fork环境;
- 作用域:方法或类;
- 实例可见JMHSample_12_Forking,JMHSample_13_RunToRun。
- 用途:以非对称的线程数运行不同的测试方法;
- 作用域:方法;
- 实例可见JMHSample_15_Asymmetric。
- 用途:用于控制测试方法的某些编译可选项;
- 作用域:方法,构造器或类;
- 实例可见JMHSample_16_CompilerControl。
- 用途:用于对状态类中的公共字段或含返回结果的方法作分别测试;
- 作用域:类;
- 实例可见JMHSample_23_AuxCounters。
- 用途:用于配置测试时,使用到的参数;
- 作用域:状态类中的非final字段;
- 实例可见JMHSample_27_Params。
- 有时候测试代码中,开发人员不希望死代码被JVM优化掉,这时可以使用
Blackhole
来消除死代码优化,实例可见JMHSample_09_Blackholes,JMHSample_21_ConsumeCPU,JMHSample_28_BlackholeHelpers。
-
大部分java应用会运行在Spring容器中,对于测试Spring应用,可以使用上述的
@Setup
和@TearDown
注解来处理,如类似下面的代码片段:@State(Scope.Benchmark) public class MySpringBeanBenchmarkTest { private ClassPathXmlApplicationContext springContext; private MySpringBean mySpringBean; @Setup public void start(){ springContext = new ClassPathXmlApplicationContext("classpath:spring-context.xml"); springContext.start(); mySpringBean = springContext.getBean(MySpringBean.class); } @TearDown public void destroy(){ springContext.destroy(); } @Benchmark @BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime}) @OutputTimeUnit(value = TimeUnit.SECONDS) public void testExecute() throws InterruptedException { Map<String, Object> context = new HashMap<>(); context.put("orderAmount", 500); mySpringBean.execute("TRADE_ORDER_0001", context); } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(".*" + MySpringBeanBenchmarkTest.class.getSimpleName() + ".*") .forks(1) .threads(Runtime.getRuntime().availableProcessors()) .measurementIterations(5) .build(); new Runner(opt).run(); } }