
JMH (Java Microbenchmark Harness)是一个用于构建、运行和分析用Java和其他针对JVM的语言编写的nano/micro/milli/macro基准测试的Java工具。OpenJDK提供的基准测试工具,是由Oracle实现JIT的相同人员开发的。
在开发过程中,为了确定一个方法的性能,很多时候都是写一个循环多次调用方法通过计算时间来查看性能是否达到预期。但这种简单的方式得出的结论往往是不准确的。现代JVM不断优化,随着代码执行次数的增加,JVM会对其进行编译优化(JIT),最终结果与简单测试得出的结果可能差距很大。此外,测试往往需要做很多额外工作,比如:控制读写线程比例,控制线程同时启动,设置多线程测试中的状态对象,统计测试结果等等,自己实现这些功能既繁琐又不能完全保证正确。而JMH就是恰好能满足上述性能测试需求的工具。
在代码进行微基准测试时必备的工具利器,在大部分优秀的框架(Netty等)都能看到针对代码性能的测试例子。或者你写了几种代码方案,但是不确定哪种方式是性能最优的,这时你可以使用JMH进行压测,就能知道哪种方案是最优的了。所以,了解和学习JMH对你是有一定的帮助的。
二、JMH常用的场景:
1、想准确的知道某个方法需要执行多长时间,以及执行时间和输入之间的相关性;
2、对比接口不同实现在给定条件下的吞吐量;
3、查看多少百分比的请求在多长时间内完成。
等等。
三、使用
目前,最新的版本是1.21。推荐使用maven的方式导入相关的依赖包:
1 <dependency>
2 <groupId>org.openjdk.jmh</groupId>
3 <artifactId>jmh-core</artifactId>
4 <version>1.21</version>
5 <scope>test</scope>
6 </dependency>
7 <dependency>
8 <groupId>org.openjdk.jmh</groupId>
9 <artifactId>jmh-generator-annprocess</artifactId>
10 <version>1.21</version>
11 <scope>test</scope>
12 </dependency>
Demo:
1import org.openjdk.jmh.annotations.Benchmark;
2import org.openjdk.jmh.annotations.BenchmarkMode;
3import org.openjdk.jmh.annotations.Mode;
4import org.openjdk.jmh.annotations.OutputTimeUnit;
5import org.openjdk.jmh.runner.Runner;
6import org.openjdk.jmh.runner.RunnerException;
7import org.openjdk.jmh.runner.options.Options;
8import org.openjdk.jmh.runner.options.OptionsBuilder;
9
10import java.util.concurrent.TimeUnit;
11
12/**
13 * String、StringBuilder、StringBuffer微基准测试
14 * @author Cocodroid
15 * @create 2018-12-02 20:56
16 */
17@BenchmarkMode(Mode.Throughput)
18@OutputTimeUnit(TimeUnit.MILLISECONDS)
19public class JMHStringTest {
20 @Benchmark
21 public String testString() {
22 String a = "";
23 for (int i = 0; i < 100; i++) {
24 a += i;
25 }
26 return a;
27 }
28
29 @Benchmark
30 public String testStringBuilder() {
31 StringBuilder sb = new StringBuilder();
32 for (int i = 0; i < 100; i++) {
33 sb.append(i);
34 }
35 return sb.toString();
36 }
37
38 @Benchmark
39 public String testStringBuffer() {
40 StringBuffer sb = new StringBuffer();
41 for (int i = 0; i < 100; i++) {
42 sb.append(i);
43 }
44 return sb.toString();
45 }
46
47 public static void main(String[] args) throws RunnerException {
48 Options opt = new OptionsBuilder()
49 .include(JMHStringTest.class.getSimpleName())
50 .forks(1) 进程数
51 .warmupIterations(3) 预热迭代次数
52 .measurementIterations(5) 度量测试迭代次数
53 .build();
54 new Runner(opt).run();
55 }
56
57}
结果:
1# JMH version: 1.21
2# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
3# VM invoker: D:\InstallSoft\Java\jdk1.8.0_131\jre\bin\java.exe
4# VM options: -javaagent:D:\InstallSoft\ideaIU-2017.1.4.win\lib\idea_rt.jar=54475:D:\InstallSoft\ideaIU-2017.1.4.win\bin -Dfile.encoding=UTF-8
5# Warmup: 3 iterations, 10 s each
6# Measurement: 5 iterations, 10 s each
7# Timeout: 10 min per iteration
8# Threads: 1 thread, will synchronize iterations
9# Benchmark mode: Throughput, ops/time
10# Benchmark: com.verysu.study.test.jmh.JMHStringTest.testString
11
12# Run progress: 0.00% complete, ETA 00:04:00
13# Fork: 1 of 1
14# Warmup Iteration 1: 246.668 ops/ms
15# Warmup Iteration 2: 288.384 ops/ms
16# Warmup Iteration 3: 290.934 ops/ms
17Iteration 1: 290.999 ops/ms
18Iteration 2: 291.096 ops/ms
19Iteration 3: 198.395 ops/ms
20Iteration 4: 290.480 ops/ms
21Iteration 5: 289.623 ops/ms
22
23
24Result "com.verysu.study.test.jmh.JMHStringTest.testString":
25 272.119 ±(99.9%) 158.711 ops/ms [Average]
26 (min, avg, max) = (198.395, 272.119, 291.096), stdev = 41.217
27 CI (99.9%): [113.407, 430.830] (assumes normal distribution)
28
29
30# JMH version: 1.21
31# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
32# VM invoker: D:\InstallSoft\Java\jdk1.8.0_131\jre\bin\java.exe
33# VM options: -javaagent:D:\InstallSoft\ideaIU-2017.1.4.win\lib\idea_rt.jar=54475:D:\InstallSoft\ideaIU-2017.1.4.win\bin -Dfile.encoding=UTF-8
34# Warmup: 3 iterations, 10 s each
35# Measurement: 5 iterations, 10 s each
36# Timeout: 10 min per iteration
37# Threads: 1 thread, will synchronize iterations
38# Benchmark mode: Throughput, ops/time
39# Benchmark: com.verysu.study.test.jmh.JMHStringTest.testStringBuffer
40
41# Run progress: 33.33% complete, ETA 00:02:43
42# Fork: 1 of 1
43# Warmup Iteration 1: 451.843 ops/ms
44# Warmup Iteration 2: 571.156 ops/ms
45# Warmup Iteration 3: 594.271 ops/ms
46Iteration 1: 594.787 ops/ms
47Iteration 2: 594.936 ops/ms
48Iteration 3: 587.871 ops/ms
49Iteration 4: 468.530 ops/ms
50Iteration 5: 546.836 ops/ms
51
52
53Result "com.verysu.study.test.jmh.JMHStringTest.testStringBuffer":
54 558.592 ±(99.9%) 208.590 ops/ms [Average]
55 (min, avg, max) = (468.530, 558.592, 594.936), stdev = 54.170
56 CI (99.9%): [350.002, 767.182] (assumes normal distribution)
57
58
59# JMH version: 1.21
60# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
61# VM invoker: D:\InstallSoft\Java\jdk1.8.0_131\jre\bin\java.exe
62# VM options: -javaagent:D:\InstallSoft\ideaIU-2017.1.4.win\lib\idea_rt.jar=54475:D:\InstallSoft\ideaIU-2017.1.4.win\bin -Dfile.encoding=UTF-8
63# Warmup: 3 iterations, 10 s each
64# Measurement: 5 iterations, 10 s each
65# Timeout: 10 min per iteration
66# Threads: 1 thread, will synchronize iterations
67# Benchmark mode: Throughput, ops/time
68# Benchmark: com.verysu.study.test.jmh.JMHStringTest.testStringBuilder
69
70# Run progress: 66.67% complete, ETA 00:01:21
71# Fork: 1 of 1
72# Warmup Iteration 1: 838.055 ops/ms
73# Warmup Iteration 2: 848.125 ops/ms
74# Warmup Iteration 3: 684.022 ops/ms
75Iteration 1: 510.202 ops/ms
76Iteration 2: 736.051 ops/ms
77Iteration 3: 713.015 ops/ms
78Iteration 4: 841.232 ops/ms
79Iteration 5: 842.549 ops/ms
80
81
82Result "com.verysu.study.test.jmh.JMHStringTest.testStringBuilder":
83 728.610 ±(99.9%) 522.563 ops/ms [Average]
84 (min, avg, max) = (510.202, 728.610, 842.549), stdev = 135.708
85 CI (99.9%): [206.047, 1251.172] (assumes normal distribution)
86
87
88# Run complete. Total time: 00:04:03
89
90REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
91why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
92experiments, perform baseline and negative tests that provide experimental control, make sure
93the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
94Do not assume the numbers tell you what you want them to tell.
95
96Benchmark Mode Cnt Score Error Units
97JMHStringTest.testString thrpt 5 272.119 ± 158.711 ops/ms
98JMHStringTest.testStringBuffer thrpt 5 558.592 ± 208.590 ops/ms
99JMHStringTest.testStringBuilder thrpt 5 728.610 ± 522.563 ops/ms
100
预热迭代3次,度量测试迭代5次,进行100次字符串的拼接操作。
从上面的测试结果来看,跟我们之前学习String、StringBuilder、StringBuffer对字符串的拼接操作,预测的性能结果很吻合:(吞吐量)StringBuilder(728) > StringBuffer(558) > String(272)。所以,你知道JMH的作用和威力了吧?
四、常用注解
1、@Benchmark
标记要测试的方法。
2、@BenchmarkMode
设置测试模式,Mode.Throughput:吞吐量模式,一个时间单位内操作数(即是本文上面的例子);Mode.AverageTime:平均执行时间模式;Mode.SampleTime:采样模式;Mode.SingleShotTime:单次操作模式;Model.All:包含上面的全部模式。
3、@OutputTimeUnit
输出结果的时间粒度,有秒、毫秒、微妙、纳秒等时间单位。
4、@State
Scope.Thread:默认的State,每个测试线程分配一个实例;
Scope.Benchmark:所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能;
Scope.Group:每个线程组共享一个实例。
5、@Fork
设置进程数,以及JVM参数。
五、资料
学习一手jmh资料还是要关注官网,官网给出的几十个例子都是非常不错的资料。
JMH官网地址:
http://openjdk.java.net/projects/code-tools/jmh/
JMH官网38个例子:
http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/
相信上面的资料文档都能给你很多帮助!
参考资料:
https://blog.csdn.net/lxbjkben/article/details/79410740
https://www.jianshu.com/p/d2517aaced40
https://mp.weixin.qq.com/s/uIhbLSlZIPypy-rs5GZndg

搬运工来架构
挨踢人聚集地,一个分享干货的公众号
作者:cocodroid
来源:https://blog.verysu.com/article/411
你可能喜欢




