news 2026/4/19 5:48:40

Java高级开发必须掌握JMH进行性能测试优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java高级开发必须掌握JMH进行性能测试优化

1. 简介

JMH(Java Microbenchmark Harness)是一种Java工具,用于构建、运行和分析用Java和其他面向JVM 的语言编写的nano/micro/milli/macro基准测试。

为什么要使用JMH?有以下几方面:

  1. 准确性:JMH是专门用于代码微基准测试的工具套件。与简单的for循环或JMeter等工具相比,JMH在测试前会预热程序,并且可以通过配置进程数、线程数等参数来使程序更接近实际的运行状况,从而提供更准确的性能测试结果。

  2. 针对性:JMH是基于方法层面的基准测试工具,允许开发者准确地知道某个方法的执行耗时,以及不同输入与最终实际耗时的关系。这使得开发者能够针对热点方法进行性能分析和优化。

  3. 功能性:JMH支持多种测试模式,如吞吐量(Throughput)、平均耗时(AverageTime)、随机采样(SampleTime)、单次执行(SingleShotTime)等,可以满足不同场景下的性能测试需求。

  4. 易用性:JMH提供了丰富的API和配置选项,使得开发者可以轻松地创建和执行基准测试。同时,JMH也支持Maven和Gradle等主流的项目管理工具,方便集成到现有的开发流程中。

注意事项:运行JMH基准测试的推荐方法是使用Maven设置依赖于应用程序的jar文件的独立项目。最好使用这种方法来确保基准测试被正确初始化并产生可靠的结果。可以从现有项目中运行基准测试,甚至可以从IDE中运行,但设置更复杂,结果更不可靠。

2. 实战案例

2.1 基于Maven构建

在命令行运行如下命令,创建一个JMH环境项目

mvn archetype:generate \ -DinteractiveMode=false \ -DarchetypeGroupId=org.openjdk.jmh \ -DarchetypeArtifactId=jmh-java-benchmark-archetype \ -DgroupId=com.pack \ -DartifactId=jhm-demo \ -Dversion=1.0

稍等片刻(需要下载相关的依赖),输出如下成功创建

接下来将项目导入的eclipse 中,导入后默认使用的jmh包是1.0版本,这里我改成最新版本1.37。

<jmh.version>1.37</jmh.version> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-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) ; }

这里测试SimpleDateFormat,LocalDateTime日期格式化的性能,测试结果:

明细高于SimpleDateFormat。

2.7 Map集合遍历测试

测试代码​​​​​​​

private List<UserDTO> 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) { List<User> ret = datas.stream().map(dto -> new User(dto.id, dto.name)).collect(Collectors.toList()) ; hole.consume(ret) ; } @Benchmark public void testParallelStream(Blackhole hole) // 多线程并行流处理 List<User> 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%免费】

​​​件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

​​​​​​​

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/19 5:48:39

MySQL搭建主从后如何校验数据一致性_使用pt-table-sync修复差异

不能盲目执行。pt-table-sync 默认只输出 SQL 不执行&#xff0c;需加 --execute&#xff1b;须确认主库唯一写入&#xff0c;否则修复语句可能错误&#xff1b;建议先用 --dry-run --print 验证&#xff0c;注意 WHERE 是否基于主键&#xff0c;跨版本同步需显式指定 --chars…

作者头像 李华
网站建设 2026/4/19 5:43:56

AI生成代码的「可信边界」在哪里?2026奇点大会联合MIT、CNCF发布《AI代码生产安全基线V1.0》:含17项静态检测阈值、3类不可自动化修复缺陷清单

第一章&#xff1a;AI生成代码的「可信边界」本质探源 2026奇点智能技术大会(https://ml-summit.org) 「可信边界」并非指模型输出是否语法正确&#xff0c;而是其行为在真实工程语境中是否可预测、可验证、可归责。这一边界根植于训练数据的隐式契约、推理过程的不可观测性&…

作者头像 李华
网站建设 2026/4/19 5:42:54

CTF隐写术新花样:用PIL库从BMP图片G通道提取隐藏压缩包(附避坑指南)

CTF隐写术实战&#xff1a;从BMP图片中提取隐藏数据的五种高阶技巧 在CTF竞赛和数字取证领域&#xff0c;BMP图片常常成为隐藏信息的理想载体。这种看似简单的位图格式&#xff0c;因其无损压缩特性和可预测的文件结构&#xff0c;为数据隐藏提供了多种可能性。本文将深入探讨五…

作者头像 李华
网站建设 2026/4/19 5:39:31

[Android] B哩B哩第三方客户端 PiliPlus 2.0.4

[Android] B哩B哩第三方客户端 PiliPlus 2.0.4 链接&#xff1a;https://pan.xunlei.com/s/VOqVHD1SPPA9vEzzRe8xXUHYA1?pwdjnfd# PiPlus是一款基于Flutter开发的第三方哗哩哔哩客户端 在核心功能保证的基础上&#xff0c;更清爽且支持原画质播放&#xff0c;有更多小功能可…

作者头像 李华