1. 简介JMHJava Microbenchmark Harness是一种Java工具用于构建、运行和分析用Java和其他面向JVM 的语言编写的nano/micro/milli/macro基准测试。为什么要使用JMH有以下几方面准确性JMH是专门用于代码微基准测试的工具套件。与简单的for循环或JMeter等工具相比JMH在测试前会预热程序并且可以通过配置进程数、线程数等参数来使程序更接近实际的运行状况从而提供更准确的性能测试结果。针对性JMH是基于方法层面的基准测试工具允许开发者准确地知道某个方法的执行耗时以及不同输入与最终实际耗时的关系。这使得开发者能够针对热点方法进行性能分析和优化。功能性JMH支持多种测试模式如吞吐量Throughput、平均耗时AverageTime、随机采样SampleTime、单次执行SingleShotTime等可以满足不同场景下的性能测试需求。易用性JMH提供了丰富的API和配置选项使得开发者可以轻松地创建和执行基准测试。同时JMH也支持Maven和Gradle等主流的项目管理工具方便集成到现有的开发流程中。注意事项运行JMH基准测试的推荐方法是使用Maven设置依赖于应用程序的jar文件的独立项目。最好使用这种方法来确保基准测试被正确初始化并产生可靠的结果。可以从现有项目中运行基准测试甚至可以从IDE中运行但设置更复杂结果更不可靠。2. 实战案例2.1 基于Maven构建在命令行运行如下命令创建一个JMH环境项目mvn archetype:generate \ -DinteractiveModefalse \ -DarchetypeGroupIdorg.openjdk.jmh \ -DarchetypeArtifactIdjmh-java-benchmark-archetype \ -DgroupIdcom.pack \ -DartifactIdjhm-demo \ -Dversion1.0稍等片刻需要下载相关的依赖输出如下成功创建接下来将项目导入的eclipse 中导入后默认使用的jmh包是1.0版本这里我改成最新版本1.37。jmh.version1.37/jmh.version dependency groupIdorg.openjdk.jmh/groupId artifactIdjmh-core/artifactId version${jmh.version}/version /dependency修改依赖版本。建立基准生成项目后可以使用以下Maven命令进行构建:mvn clean verify稍等片刻后输出如下构建成功生成的Jar包在target目录下运行基准测试构建完成后将获得自包含的可执行JAR其中包含基准测试以及所有必要的JMH基础架构代码通过如下命令运行java -jar target/benchmarks.jar执行结果如下以上是基于maven打成jar包进行测试这也是官方推荐也是最准的测试方式。2.2 直接通过Main运行每次通过打成jar包的方式运行比较的麻烦可以通过项目中main函数的方式直接运行Benchmark public void testMethod() { // 你的测试代码 } public static void main(String[] args) throws Exception { Options options new OptionsBuilder() // 你要测试的类 .include(MyBenchmark.class.getSimpleName()) // 启动几个进程 .forks(1) .build() ; new Runner(options).run() ; }通过该种方式也可以但是测试的准确性没有打成jar的方式准确。以上是通过JMH基准测试的方式。接下来对JMH其它的配置及测试进行更多的介绍。2.3 核心注解// 预热1s钟预热5次 Warmup(iterations 10, time 1) // 启动多少个进程 Fork(value 1, jvmArgsAppend {-Xms512m, -Xmx512m}) // 指定显示结果枚举值 BenchmarkMode(Mode.AverageTime) // 指定显示结果单位枚举值 OutputTimeUnit(TimeUnit.NANOSECONDS) // 变量共享范围(整个测试过程中共享还是在单个线程中共享) State(Scope.Benchmark) public class MyBenchmark { // TODO }Warmup(iterations 10, time 1)预热1s钟预热5次该注解也可以放到方法上只对当前方法生效。Fork(value 1, jvmArgsAppend {-Xms512m, -Xmx512m})启动多少个进程BenchmarkMode(Mode.AverageTime)指定显示结果枚举值OutputTimeUnit(TimeUnit.NANOSECONDS)指定显示结果单位枚举值State(Scope.Benchmark)变量共享范围(整个测试过程中共享还是在单个线程中共享)通过上面的配置默认生成的测试方法如下根据实际情况通过注解调整不同的参数配置调整。通过上面的配置明细在运行速度上比默认的快了很多。2.4 死代码问题先来对比2个测试如下测试方法​​​​​​​// 返回结果有使用i这个变量 Benchmark public int testMethodReturnResult() { int i 0; i ; return i ; } // 没有返回结果似乎没有任何意义 Benchmark public void testMethodNoReturn() { int i 0; i ; }最终测试结果如下非常明显没有返回结果的运行效率远高于有返回结果的方法。这是由于JIT会优化掉这里没有返回结果中的i相关的操作认为没有任何意义。这也就是死代码。2.5 黑洞消费值上面测试方法是只有一个变量i直接返回即可如果这里有多个值情况则可以通过黑洞Blackhole进行消费。​​​​​​​Benchmark public void testMethodMultiVariable(Blackhole hole) { int i 0; i ; int j 0 ; j ; hole.consume(i) ; hole.consume(j) ; }通过Blackhole就解决了多个变量问题。2.5 注解Setup该注解会在每个基准测试方法执行前执行。​​​​​​​Setup public void init() { System.out.println(测试前的准备...) ; }运行结果每对一个方法进行测试前都会执行Setup注解的方法。2.6 日期格式化测试​​​​​​​Benchmark public void testSimpleDateFormat(Blackhole hole) { SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss) ; String ret sdf.format(new Date()) ; hole.consume(ret) ; } Benchmark public void testDateTimeFormatter(Blackhole hole) { String ret LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss)) ; hole.consume(ret) ; }这里测试SimpleDateFormatLocalDateTime日期格式化的性能测试结果明细高于SimpleDateFormat。2.7 Map集合遍历测试测试代码​​​​​​​private ListUserDTO datas new ArrayList() ; Setup public void init() { // System.out.println(测试前的准备...) ; for (int i 0;i 100_000; i) { datas.add(new UserDTO(i 0L, 姓名 - i)) ; } } Benchmark public void testStream(Blackhole hole) { ListUser ret datas.stream().map(dto - new User(dto.id, dto.name)).collect(Collectors.toList()) ; hole.consume(ret) ; } Benchmark public void testParallelStream(Blackhole hole) // 多线程并行流处理 ListUser ret datas.parallelStream().map(dto - new User(dto.id, dto.name)).collect(Collectors.toList()) ; hole.consume(ret) ; }测试结果2.8 整合SpringBoot测试​​​​​​​private PersonService ps ; private ConfigurableApplicationContext context ; Setup public void init() { context SpringApplication.run(SpringbootDbApplication.class) ; ps context.getBean(PersonService.class) ; } Benchmark public void testSave() { Person person new Person() ; int num new Random().nextInt(100) ; person.setAge(num) ; person.setName(姓名 - num) ; this.ps.save(new Person(77, 嘿嘿)) ; } Benchmark public void testQuery(Blackhole hole) { Person p this.ps.findById(30) ; hole.consume(p) ; } TearDown public void down() { this.context.close() ; }注意端口要设置为随机值否则在有多个测试方法时会报错。测试结果以上是本篇文章的全部内容如对你有帮助就请作者吃个棒棒糖。最后下方这份完整的软件测试 视频教程已经整理上传完成需要的朋友们可以自行领取【保证100%免费】​​​软件测试面试文档我们学习必然是为了找到高薪的工作下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料并且有字节大佬给出了权威的解答刷完这一套面试资料相信大家都能找到满意的工作。​​​​​​​