一、简介
OpenJDK 中的开源项目 JMH(Java Microbenchmark Harness)。JMH 是一个面向 Java 语言或者其他 Java 虚拟机语言的性能基准测试框架。它针对的是纳秒级别、微秒级别、毫秒级别,以及秒级别的性能测试。
二、入门
1、maven依赖导入
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.19</version>
<scope>provided</scope>
</dependency>
2、例子
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import org.joda.time.DateTime;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@State(Scope.Benchmark)
public class JMHTest {
static int millis = 24 * 3600 * 1000;
public static void main(String[] args) throws Exception {
Options options = new OptionsBuilder().include(JMHTest.class.getName()).warmupIterations(3)
.measurementIterations(5).measurementTime(
TimeValue.seconds(10)).forks(2).build();
new Runner(options).run();
}
@Benchmark
@Threads(5)
public void runCalendar() {
Calendar calendar = Calendar.getInstance();
}
@Benchmark
@Threads(5)
public void runJoda() {
DateTime dateTime = new DateTime();
}
@Benchmark
@Threads(5)
public void runSystem() {
long result = System.currentTimeMillis() / millis;
}
}
3、运行方式
(1)运行JMHTest.main()方法
(2)在Terminal控制台输入mvn clean install(package),对项目进行打包;再输入java -jar ./target/benchmarks.jar运行
4、注解含义
(1)@BenchmarkMode 基准测试类型
*Mode.Throughput 整体吞吐量,例如“1秒内可以执行多少次调用”。
*Mode.AverageTime 调用的平均时间,例如“每次调用平均耗时xxx毫秒”
*Mode.SampleTime 随机取样,最后输出取样结果的分布,例如“99%的调用在xxx毫秒以内,99.99%的调用在xxx毫秒以内”
*Mode.SingleShotTime 以上模式都是默认一次 iteration 是 1s,唯有 SingleShotTime 是只运行一次。往往同时把 warmup 次数设为0,用于测试冷启动时的性能
*Mode.All 所有模式依次运行
*Iteration 是 JMH 进行测试的最小单位。在大部分模式下,一次 iteration 代表的是一秒,JMH 会在这一秒内不断调用需要 benchmark 的方法,然后根据模式对其采样,计算吞吐量,计算平均执行时间等。
(2)@Warmup
进行基准测试前需要进行预热。一般我们前几次进行程序测试的时候都会比较慢,所以要让程序进行几轮预热,保证测试的准确性。其中的参数iterations也就非常好理解了,就是预热轮数。
(3)@Fork
需要运行的试验(迭代集合)数量。每个试验运行在单独的JVM进程中。也可以指定(额外的)JVM参数。
(4)@OutputTimeUnit
benchmark 结果所使用的时间单位,可用于类或者方法注解,使用java.util.concurrent.TimeUnit中的标准时间单位。
(5)@Benchmark
方法注解,表示该方法是需要进行 benchmark 的对象。
(6)@Setup
方法注解,会在执行 benchmark 之前被执行,正如其名,主要用于初始化。
(7)@State
注解定义了给定类实例的可用范围。JMH可以在多线程同时运行的环境测试,因此需要选择正确的状态。当使用@Setup参数的时候,必须在类上加这个参数,不然会提示无法运行。
Scope.Thread 默认状态。实例将分配给运行给定测试的每个线程。
Scope.Benchmark 运行相同测试的所有线程将共享实例。可以用来测试状态对象的多线程性能(或者仅标记该范围的基准)。
Scope.Group 实例分配给每个线程组(查看后面的线程组部分)除了将单独的类标记@State,也可以将你自己的benchmark类使用
(8)@Threads
每个进程中的测试线程,可用于类或者方法上。一般选择为cpu乘以2。如果配置了 Threads.MAX ,代表使用 Runtime.getRuntime().availableProcessors() 个线程。
(9)@Measurement
度量,其实就是一些基本的测试参数。iterations进行测试的轮次,time每轮进行的时长,timeUnit时长单位。都是一些基本的参数,可以根据具体情况调整。一般比较重的东西可以进行大量的测试,放到服务器上运行。
5、测试结果
结果显示吞吐量Calendar方法每毫秒12768次调用;Joda每毫秒153347次调用