当前位置: 首页 > news >正文

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%免费】

​​​件测试面试文档

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

​​​​​​​

http://www.jsqmd.com/news/664421/

相关文章:

  • 灵毓秀-牧神-造相Z-Turbo问题解决:常见生成错误与快速修复方法
  • 卡证检测矫正模型对抗攻击与安全加固研究
  • Qwen3.5-9B-AWQ-4bit部署教程:基于CSDN GPU平台的7860端口快速访问指南
  • 想了解钕铁硼磁铁厂家?2026年3月口碑推荐别错过,电机磁铁/粘结钕铁硼/永磁铁氧体,钕铁硼磁铁厂家推荐分析 - 品牌推荐师
  • ISOWorkshop光盘镜像处理工具:制作光盘镜像与ISO解压提取的完整指南
  • 2026年3月国内知名的选果机企业哪家好,AI无损测糖分选机/无损选果机/AI智能无损选果机/冬枣选果机,选果机产品推荐 - 品牌推荐师
  • GPU显存稳定性终极检测:memtest_vulkan如何彻底解决硬件诊断难题
  • Qwen3-TTS多语种语音合成效果展示:俄文/葡文/意文真实音频对比
  • DeOldify图像上色数据隐私:所有处理在本地完成,无云端传输保障合规
  • DAMOYOLO-S模型轻量化新星YOLOv11对比评测:速度与精度的权衡
  • SDXL 1.0创意工作流:结合Vue.js构建可视化控制面板
  • 如何用嘎嘎降AI处理经济学论文:数据表格和经济模型保留操作指南
  • 2026年3月商标购买企业推荐,买服装商标/商标转让购买/专业商标买卖/商标安全交易/商标交易转让,商标购买机构怎么选择 - 品牌推荐师
  • MySQL搭建主从后如何校验数据一致性_使用pt-table-sync修复差异
  • 美胸-年美-造相Z-Turbo效果实测:LoRA微调后对‘自然感’‘呼吸感’‘动态感’的增强表现
  • Visio图表智能生成:基于Phi-4-mini-reasoning描述自动创建流程图与架构图
  • 京东自动化抢购脚本终极指南:JDspyder让热门商品抢购变得简单
  • RMBG-1.4开源模型部署:AI净界支持JPG/PNG批量处理实操指南
  • RTX 4090D专用PyTorch 2.8镜像:torch.compile+FlashAttention-2性能对比
  • 如何用嘎嘎降AI批量处理多章节论文:章节分段上传操作教程
  • 告别“玄学”调试:用VS Code+GDB稳定调试C/C++项目的完整工作流搭建指南
  • 像素时装锻造坊GPU算力优化:双卡负载均衡与显存占用监控实操指南
  • 魔幻C++ 3229:【例38.3】 X战警
  • 创新网页数据采集工具深度解析:零代码内容提取完整解决方案
  • Pixel Script Temple 赋能后端开发:API接口文档与Mock服务器脚本自动生成
  • 安全测试工具大全
  • PDF-Extract-Kit-1.0处理古籍文献:特殊字符与版式识别
  • [Android] 轻量化电视TV版抖音APP——myDV Lite_v1.3.0
  • 为什么你的Copilot总生成“能跑但不能上线”的代码?SITS2026定义行业首个《AI生成移动端代码可信度分级标准V1.2》
  • 智能眼镜成主流趋势:时尚与科技品牌纷纷入局,苹果或明年推出自家产品