开始使用#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <jmh.version>1.23</jmh.version>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
|
可以使用maven archtype进行自动生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@BenchmarkMode(Mode.Throughput) // 吞吐量
@OutputTimeUnit(TimeUnit.MILLISECONDS) // 结果所使用的时间单位
@State(Scope.Thread) // 每个测试线程分配一个实例
@Fork(2) // Fork进行的数目
@Warmup(iterations = 2) // 先预热4轮
@Measurement(iterations = 5) // 进行10轮测试
public class MyBenchmark {
static AtomicInteger integer = new AtomicInteger();
@Benchmark
public void testMethod() {
integer.incrementAndGet();
}
}
|
- 可以编译为jar包,直接执行
1
2
3
| mvn clean package
java -jar target/benchmark.jar
|
- 也可以编写main方法, 直接执行
1
2
3
4
5
6
7
8
9
10
11
12
| public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(MyBenchmark.class.getSimpleName())
.forks(1)
.measurementIterations(3)
.warmupIterations(2)
.resultFormat(ResultFormatType.JSON)
.result("log/benchmark_sequence.json")
.output("log/benchmark_sequence.log")
.build();
new Runner(opt).run();
}
|
上面显示: 执行MyBenchmark
, 使用一个进程执行, 压力测试3轮, 预热2轮output输出到log/benchmark_sequence.log
文件, result使用 json格式化, 并输出到log/benchmark_sequence.json
文件.
注意 : include 输入的是正则表达式.
输出结果#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
| # JMH version: 1.23
# VM version: JDK 1.8.0_144, Java HotSpot(TM) 64-Bit Server VM, 25.144-b01
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Users/black/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/193.5662.53/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=53676:/Users/black/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/193.5662.53/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 1 iterations, 10 s each
# Measurement: 2 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.black.demo.MyBenchmark.testMethod
# Run progress: 0.00% complete, ETA 00:02:00
# Fork: 1 of 2
objc[75400]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/bin/java (0x10c7bc4c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10dff54e0). One of the two will be used. Which one is undefined.
# Warmup Iteration 1: 139598701.042 ops/s
Iteration 1: 152859172.038 ops/s
Iteration 2: 86876920.802 ops/s
# Run progress: 25.00% complete, ETA 00:01:34
# Fork: 2 of 2
objc[75523]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/bin/java (0x10501e4c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x1050944e0). One of the two will be used. Which one is undefined.
# Warmup Iteration 1: 147700155.776 ops/s
Iteration 1: 136090040.091 ops/s
Iteration 2: 147411608.766 ops/s
Result "com.black.demo.MyBenchmark.testMethod":
130809435.424 ±(99.9%) 194568258.201 ops/s [Average]
(min, avg, max) = (86876920.802, 130809435.424, 152859172.038), stdev = 30109653.331
CI (99.9%): [≈ 0, 325377693.625] (assumes normal distribution)
# JMH version: 1.23
# VM version: JDK 1.8.0_144, Java HotSpot(TM) 64-Bit Server VM, 25.144-b01
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Users/black/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/193.5662.53/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=53676:/Users/black/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/193.5662.53/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 1 iterations, 10 s each
# Measurement: 2 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.black.demo.MyBenchmark2.testMethod
# Run progress: 50.00% complete, ETA 00:01:02
# Fork: 1 of 2
objc[75656]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/bin/java (0x10df724c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10dfe84e0). One of the two will be used. Which one is undefined.
# Warmup Iteration 1: 143550017.896 ops/s
Iteration 1: 144959710.003 ops/s
Iteration 2: 143473054.324 ops/s
# Run progress: 75.00% complete, ETA 00:00:31
# Fork: 2 of 2
objc[75787]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/bin/java (0x10cf884c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10e7c04e0). One of the two will be used. Which one is undefined.
# Warmup Iteration 1: 149558611.229 ops/s
Iteration 1: 152600541.752 ops/s
Iteration 2: 151566800.779 ops/s
Result "com.black.demo.MyBenchmark2.testMethod":
148150026.714 ±(99.9%) 29737702.401 ops/s [Average]
(min, avg, max) = (143473054.324, 148150026.714, 152600541.752), stdev = 4601942.364
CI (99.9%): [118412324.313, 177887729.116] (assumes normal distribution)
# Run complete. Total time: 00:02:04
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
Benchmark Mode Cnt Score Error Units
MyBenchmark.testMethod thrpt 4 130809435.424 ± 194568258.201 ops/s
MyBenchmark2.testMethod thrpt 4 148150026.714 ± 29737702.401 ops/s
Benchmark result is saved to log/benchmark_test.json
|
Benchmark | Mode | Cnt | Score | Error | Units |
---|
基准测试执行的方法 | 测试模式,这里是吞吐量 | 运行多少次 | 分数 | 错误 | 单位 |
MyBenchmark.testMethod | thrpt | 4 | 130809435.424±194568258.201 | | ops/s |
MyBenchmark2.testMethod | thrpt | 4 | 148150026.714±29737702.401 | | ops/s |
注意: 最后Error 列输出为空, ±194568258.201 指的是Score列的值.
可视化展示#
可以上传JSON文件到 https://jmh.morethan.io/
或 http://deepoove.com/jmh-visual-chart/
进行可视化对比

注解使用#
@BenchmarkMode#
Mode 表示 JMH 进行 Benchmark 时所使用的模式。通常是测量的维度不同,或是测量的方式不同。目前 JMH 共有四种模式:
Throughput
: 整体吞吐量,例如“1秒内可以执行多少次调用”,单位是操作数/时间。AverageTime
: 调用的平均时间,例如“每次调用平均耗时xxx毫秒”,单位是时间/操作数。SampleTime
: 随机取样,最后输出取样结果的分布,例如“99%的调用在xxx
毫秒以内,99.99%
的调用在xxx
毫秒以内”SingleShotTime
: 以上模式都是默认一次 iteration
是 1s,唯有 SingleShotTime
是只运行一次。往往同时把 warmup
次数设为0,用于测试冷启动时的性能
@OutputTimeUnit
#
结果所使用的时间单位
@WarmUp
#
Warmup
是指在实际进行 Benchmark
前先进行预热的行为。
为什么需要预热?因为 JVM
的 JIT
机制的存在,如果某个函数被调用多次之后,JVM
会尝试将其编译成为机器码从而提高执行速度。为了让 Benchmark
的结果更加接近真实情况就需要进行预热。
@State
#
类注解,JMH
测试类必须使用 @State
注解,它定义了一个类实例的生命周期,由于 JMH
允许多线程同时执行测试,不同的选项含义如下:
Scope.Thread
:默认的 State
,每个测试线程分配一个实例;Scope.Benchmark
:所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能;Scope.Group
:每个线程组共享一个实例;
@Fork
#
进行 fork
的次数。如果 fork
数是2的话,则 JMH
会 fork
出两个进程来进行测试。
@Meansurement
#
提供真正的测试阶段参数。指定迭代的次数,每次迭代的运行时间和每次迭代测试调用的数量(通常使用 @BenchmarkMode(Mode.SingleShotTime)
测试一组操作的开销——而不使用循环)
iterations
@return Number of measurement iterationsbatchSize
@return Batch size: number of benchmark method calls per operation
@Setup
#
方法注解,会在执行 benchmark
之前被执行,正如其名,主要用于初始化。
@TearDown
#
方法注解,与@Setup
相对的,会在所有 benchmark
执行结束以后执行,主要用于资源的回收等。
@Setup/@TearDown
注解使用Level
参数来指定何时调用fixture
:
Level.Trial
默认level
。全部benchmark
运行(一组迭代)之前/之后Level.Iteration
一次迭代之前/之后(一组调用)Level.Invocation
每个方法调用之前/之后(不推荐使用,除非你清楚这样做的目的)
@Benchmark
#
方法注解,表示该方法是需要进行 benchmark
的对象。
@Param
#
成员注解,可以用来指定某项参数的多种情况。特别适合用来测试一个函数在不同的参数输入的情况下的性能。@Param
注解接收一个String
数组,在 @Setup
方法执行前转化为为对应的数据类型。多个 @Param
注解的成员之间是乘积关系,譬如有两个用 @Param
注解的字段,第一个有5
个值,第二个字段有2
个值,那么每个测试方法会跑5*2=10
次。
@Threads
#
每个fork进程使用多少条线程去执行你的测试方法, 默认是Runtime.getRuntime().availableProcessors()
,
OptionsBuilder 和 注解对比#
方法名 | 参数 | 作用 | 对应注解 |
---|
include | 要运行基准测试类的简单名称eg.StringConnectBenchmark | 指定要运行的基准测试类 | - |
exclude | 不要运行基准测试类的简单名称eg.StringConnectBenchmark | 指定不要运行的基准测试类 | - |
warmupIterations | 预热的迭代次数 | 指定预热的迭代次数 | @Warmup |
warmupBatchSize | 预热批量的大小 | 指定预热批量的大小 | @Warmup |
warmupForks | 预热模式:INDI,BULK,BULK_INDI | 指定预热模式 | @Warmup |
warmupMode | 预热的模式 | 指定预热的模式 | @Warmup |
warmupTime | 预热的时间 | 指定预热的时间 | @Warmup |
measurementIterations | 测试的迭代次数 | 指定测试的迭代次数 | @Measurement |
measurementBatchSize | 测试批量的大小 | 指定测试批量的大小 | @Measurement |
measurementTime | 测试的时间 | 指定测试的时间 | @Measurement |
mode | 测试模式:Throughput(吞吐量),AverageTime(平均时间),SampleTime(在测试中,随机进行采样执行的时间),SingleShotTime(在每次执行中计算耗时),All | 指定测试的模式 | @BenchmarkMode |
JMH Benchmark