Java JMH 基准测试工具

任何新工具的出现 , 都是为了解决某个具体问题而诞生的 , 否则就没有存在的必要了
本文章将会概述一下 基准测试的概念 、StopWatch的基本使用、 JMH的基本使用与用户JMH中常用注解概述 。

Java JMH 基准测试工具

文章插图
 
 
1 引言JMH 全称 JAVA Microbenchmark Harness  , Microbenchmark 可解析为 短语 micro-benchmark 测试 , Microbenchmark也可解析为 micro(基本的)benchmark(标准检查程序)。
JMH 是由 Java Jvm 虚拟机团队开发  , 在Jvm 对 Java 文件的编译阶段、类的加载阶段、运行阶段者有持续的不同程度的优化 , JMH的诞生就是为了让 Java 开发者能够了解到自己所编写的代码运行的情况 , 以及性能方面的情况 。
1.1 基准测试 ?基准测试是指通过设计科学的测试方法、测试工具和测试系统 , 实现对一类测试对象的某项性能指标进行定量的和可对比的测试 。
1.2 使用 StopWatch 来进行测试时间计算一个常见的问题 就是 我们会说 ArrayList 比 LinkedList 性能好点 , 那么我们总会要想方法去测试一下 , 如添加 1000 0000 条数据 , 看谁消耗的时间少 ,  StopWatch 用来记录这个时间差并可生成对比 , 如下代码清单 1-1 中所示的测试用例中 , 分别向 ArrayList 、LinkedList 中添加了 1000 0000 条数据 , 然后通过 StopWatch 来生成时间消耗对比:
【Java JMH 基准测试工具】///代码清单 1-1 package com.example.demo;import org.junit.jupiter.api.Test;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.util.StopWatch;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;@SpringBootTestclass DemoApplicationTest2 {private static final Logger LOG = LoggerFactory.getLogger(DemoApplicationTest2.class);@Testvoid testArrayAndLinkedList() {List<String> arrayList = new ArrayList<>();StopWatch stopWatch = new StopWatch();//开始计时stopWatch.start("arrayList 测试");for (int i = 0; i < 10000000; i++) {arrayList.add("测试数据");}///停止计时stopWatch.stop();//测试 LinkedListList<String> linkedList = new LinkedList<>();//开始计时stopWatch.start("linkedList 测试");for (int i = 0; i < 10000000; i++) {linkedList.add("测试数据");}///停止计时stopWatch.stop();LOG.info("arrayList 消耗的总时间 " + stopWatch.prettyPrint());LOG.info("arrayList 消耗的总时间 " + stopWatch.getTotalTimeMillis());}}然后执行单元测试后生成 如下结果:
Java JMH 基准测试工具

文章插图
 
很明显 对于add方法来讲 , ArrayList 的性能要比 LinkedList 的性能要好点 。
在这里只是一个粗糙的测试方法 , 因为:
  • 1. 使用到的 StopWatch  , 在其内部也会记录方法的开始的纳秒数 , 这种操作也会消耗一定的CPU时间 。
    2.JVM 在运行时对 for 循环也有优化 , 这样就会导致测试时间包含了一部分JVM性能优化的执行时间3.前后运行的 JVM 环境并不完全相同
所以为了能更严谨的来进行测试 ,  JMH 就出现了 。
2 JMH 基本使用2.1 集成JMH是 JDK9自带的 , 如果你是 JDK9 之前的版本也可以通过导入 openjdk
<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></dependency>2.2 使用 JMH 进行测试///代码清单 2-1import org.openjdk.jmh.annotations.*;import org.openjdk.jmh.runner.Runner;import org.openjdk.jmh.runner.options.Options;import org.openjdk.jmh.runner.options.OptionsBuilder;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.concurrent.TimeUnit;//Mode 表示 JMH 进行 Benchmark 时所使用的模式//BenchmarkMode的value是一个数组 , 可以把几种Mode集合在一起执行 , 还可以设置为Mode.Al@BenchmarkMode(Mode.AverageTime)//benchmark 结果所使用的时间单位//使用java.util.concurrent.TimeUnit中的标准时间单位// 微秒@OutputTimeUnit(TimeUnit.MICROSECONDS)///JMH测试类必须使用@State注解 , // State定义了一个类实例的生命周期 , // 可以类比Spring Bean的Scope@State(Scope.Thread)public class DemoApplicationTestJMH {public static void main(String[] args) throws Exception {String name = DemoApplicationTestJMH.class.getName();Options options = new OptionsBuilder().include(name ).forks(1).measurementIterations(3).warmupIterations(3).build();new Runner(options).run();}@Benchmarkpublic void testArrayList() {List<String> arrayList = new ArrayList<>();for (int i = 0; i < 10000000; i++) {arrayList.add("测试数据");}}@Benchmarkpublic void testLinkedList() {//测试 LinkedListList<String> linkedList = new LinkedList<>();for (int i = 0; i < 10000000; i++) {linkedList.add("测试数据");}}}


推荐阅读