JDK9新特性


发布于 2025-10-16 / 3 阅读 / 0 评论 /
JDK9版本新特性介绍

以下介绍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();
	}
}