以下介绍JDK9版本的新特性。
JMH
JMH是JDK9提供的新特性。
JMH简介
JMH(Java Microbenchmark Harness)的使用方法及注意事项,JMH是JDK9中用于微基准测试的工具,能帮助开发者准确测量代码片段的性能
JMH使用场景
当我们写代码的时候总会遇到一些选择上的困惑,比如数据库 因连接池 Druid只和HikariCP到底哪一个效率更高,或者是LinkedList和ArrayList哪个在特定的场景下更快,再比如找到系统性能的瓶颈所在,但是具体是哪个方法哪条语句执行太慢而导致的?
上面提到的场景都可以通过JMH来完成,JMH是个用于java或者其他JVM语言的,提供构建,运行和分析(按照多种基准:纳秒、微妙、毫秒)的工具。
JMH的使用
JMH需要在源码中使用,通过以下方式实现一个简单的demo。
dependency引入
需要给项目添加依赖
<properties>
<jmh.version>1.37</jmh.version>
</properties>
<dependencies>
<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>
</dependencies>下面就可以在源码中使用相关注解,对响应方法进行测试。
代码中使用
如下所示:
@State(Scope.Thread)
public class TestBenchmark {
@Benchmark
@warmup(iterations = 4)
@Measurement(iterations = 4)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
public long testMethod() {
return System.currentTimeMillis();
}
}上述源码使用了7个注解
(1)@State
State标注在某一个类上,用于声明该类型对象的范围,我们测试标注为benchmark的方法,其实就是创建该类的实例对象过,然后通过多线程调用此方法。
如果标注为@State(Scope.Thread),则为每一个线程创建一个对象.如果标注为@State(Scope.Benchmark),则创建一个对象,所线程间共享。如果标注为@State(Scope.Group),每个线程组共享一个实例。
(2)@Benchmark
Benchmark标签是用来标记测试方法的,只有被这个注解标记的话,该方法才会参与基准测试,但是有一个基本的原则就是被@Benchmark标记的方法必须是public的。安装了JMH idea插件才可以直接执行,否则需要通过main方法。
(3)@Warmup
@Warmup用来配置预热的内容,可用于类或者方法上,越靠近执行方法的地方越准确。一般配置warmup的参数有这些
iterations: 预热的次数
time: 每次预热的时间,默认是1。
timeUnit: 时间单位,默认是s,默认是sec.
batchSize: 批处理大小,每次操作调用几次方法
(4)@Measurement
用来控制实际执行的内容,配置的选项本warmup一样
(5)@BenchmarkMode
表示 JMH 进行 Benchmark 时所使用的模式
(6)@OutputTimeUnit
@OutputTimeunit代表测量的单位,比如秒级别,毫秒级别,微妙级别等等。一般都使用微妙和毫秒级别的稍微多一点。该注解可以用在方法级别和类级别。当用在举级别的时候会被更加精确的方法级别的注解覆盖,原则就是离目标更近的注解更容易生效。
(7)@Setup
@Setup 会在执行 benchmark 之前被执行,正如其名,主要用于初始化
(8)@TearDown
@TearDown 和 @Setup 相对的,会在所有 benchmark 执行结束以后执行,主要用于资源的回收等
(9)@Group
方法注解,可以把多个 benchmak 定义为同一个 group,则它们会被同时执行,警如用来模拟生产者-消费者读写速度不一致情况下的表现。使用@GroupThreads(threadsNumber)注解标记每个测试,指定运行给定方法的线程数量。默认是1。
测试启动方式
通过插件启动每次只能测试一个标注为benchmark的方法,假如需要有多个方法则只能通过main方法启动。
以下是通过main函数启动的案例
@State(Scope.Thread)
public class MyBenchmark {
@Benchmark
public long test1() {
return System.currentTimeMillis();
}
@Benchmark
public long test2() {
return Calendar.getInstance().getTimeInMillis();
}
public static void main(String[] args) throws RunnerException {
Options ops = new OptionsBuilder()
.include(MyBenchmark.class.getName()) // include @Benchmark 所在的类的名字
.forks(1)
.warmupIterations(5) // 预热的次数
.measurementIterations(5) // 实际测量的送代次数。
.threads(2)
.build();
new Runner(ops).run();
}
}