什么是JMH

​ JMH,即Java Microbenchmark Harness,Java平台下的一套微基准测试工具。如果我们需要测试API性能的话,就可以用上这个工具,所以它并不是取代单元测试的。它可以在开发阶段提供性能参考标准,不过这并不代表线上的性能表现,不同的硬件和操作系统也会带来性能差异,所以最终还是需要上到测试或沙箱环境,让测试人员进行压测。

为什么需要JMH

​ 在了解JMH之前,如果需要性能测试,我们通常会使用for循环,或者JMeter。而JMH正是比for循环严谨,比JMeter使用简单的测试工具。

​ 再者,不知道你注意过没有,在使用for循环测试时,第一次或者头几次运行总是最慢的,越到后面越快。从《计算机组成与设计 硬件软件接口》一书中可以了解到,从更底层讲,Java是解释型的语言。虽然Java也需要编译,但是编译后只是字节码,还需要JVM解释成对应宿主机的机器码。解释的优势是可移植性,但是性能较差。在20世纪80和90年代,虽然解释型语言的性能也飞速提升,但是与C语言相比,仍有10倍的性能差距。

为了保持可移植性,同时又提高性能,Java便开发了即时编译器(Just In Time complier),其通过记录运行的程序来找到所谓的“热点”方法,然后将它们直接编译成宿主机的指令序列,即不通过JVM解释那一层。这样以后该方法的运行就会更快。

​ 看到这里也就明白了,为什么程序越到后面就会越快。JMH在真正的测试之前会预热程序,而且还可以通过配置进程数、线程数等参数来使程序更接近实际的运行状况。

如何使用

​ 首先引入Maven依赖:

	<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.21</version>
<scope>test</scope>
</dependency>

​ 本案例中,我写了一个简单的小程序,它会从指定目录读取文件夹内容(每行一个数字),然后会对取出来的数字进行排序。排序算法选择了插入排序和归并排序,我们通过基准测试来看看两者的性能差距。

​ 读取文件内容

public class ReadFile {
public static int[] readInteger(String path){
try(BufferedReader in = new BufferedReader(new FileReader(path));) {
List<Integer> temp = new ArrayList<>();
String str;
while ((str = in.readLine()) != null) {
temp.add(Integer.parseInt(str));
}
int[] result = new int[temp.size()];
for(int i=0;i<temp.size();i++){
result[i]=temp.get(i);
}
return result;
} catch (Exception e) {
e.printStackTrace();
return new int[0];
}
}
}

​ 两个排序算法就不贴了,网上可以搜到很多。实际的开发可能会用上SpringBoot,所以还得与Junit整合,并使用自动注入功能。先直接贴上测试代码:

@BenchmarkMode(Mode.All)
@Warmup(iterations = 3)//预热轮数
@Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
@Threads(8)//线程数
@Fork(0) //fork的次数,如果想用Autowired自动注入,这个填0
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@RunWith(SpringRunner.class) //整合SpringBoot的测试运行环境
@SpringBootTest
public class JHMTest {
//想用自动注入功能,对象必须是静态的,fork填0
private static SortService service; @Autowired
void setService(SortService service){
JHMTest.service =service;
} @Test
public void executeBenchmark() throws RunnerException {
//JMH的选项配置,除了上面的注解方式的配置,也可以直接在这个Options里面配置。
//其中/Users/xxxx/Desktop/Benchmark.json是结果的输出文件
Options options = new OptionsBuilder().include(this.getClass().getSimpleName())
.output("/Users/xxxx/Desktop/Benchmark.json").build();
new Runner(options).run();
} @Benchmark
public void insertSortTest(){
int[] arr = ReadFile.readInteger("/Users/xxxxx/Desktop/test.txt");
service.insertSort(arr);
} @Benchmark
public void mergeSortTest(){
int[] arr = ReadFile.readInteger("/Users/xxxxx/Desktop/test 2.txt");
service.mergeSort(arr);
}
}

​ 上面注释简单写了几个关键点,我们执行executeBenchmark方法,JMH就会执行该类下带有Benchmark注解的方法。最终结果会输出到指定文件中

​ 其他注解的解释可见图

截图截取自:https://dunwu.github.io/javatech/test/jmh.html#jmh-api

结果查看

​ 打开结果文件,前面一大坨是系统信息,可以简单看看,直接拉到最后,结果如下:

Benchmark                                        Mode     Cnt    Score   Error   Units
JHMTest.insertSortTest thrpt 129.302 ops/ms
JHMTest.mergeSortTest thrpt 122.224 ops/ms
JHMTest.insertSortTest avgt 0.065 ms/op
JHMTest.mergeSortTest avgt 0.066 ms/op
JHMTest.insertSortTest sample 122410 0.066 ± 0.002 ms/op
JHMTest.insertSortTest:insertSortTest·p0.00 sample 0.014 ms/op
JHMTest.insertSortTest:insertSortTest·p0.50 sample 0.050 ms/op
JHMTest.insertSortTest:insertSortTest·p0.90 sample 0.106 ms/op
JHMTest.insertSortTest:insertSortTest·p0.95 sample 0.120 ms/op
JHMTest.insertSortTest:insertSortTest·p0.99 sample 0.192 ms/op
JHMTest.insertSortTest:insertSortTest·p0.999 sample 0.492 ms/op
JHMTest.insertSortTest:insertSortTest·p0.9999 sample 11.891 ms/op
JHMTest.insertSortTest:insertSortTest·p1.00 sample 17.334 ms/op
JHMTest.mergeSortTest sample 122055 0.066 ± 0.002 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.00 sample 0.014 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.50 sample 0.050 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.90 sample 0.107 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.95 sample 0.121 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.99 sample 0.187 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.999 sample 0.457 ms/op
JHMTest.mergeSortTest:mergeSortTest·p0.9999 sample 11.957 ms/op
JHMTest.mergeSortTest:mergeSortTest·p1.00 sample 12.419 ms/op
JHMTest.insertSortTest ss 0.020 ms/op
JHMTest.mergeSortTest ss 0.020 ms/op

​ 结果如上,Mode中thrpt代表吞吐量,单位时间内的执行次数。avgt是平均时间,一次执行需要的单位时间。sample是基于采样的执行时间,采样频率由JMH自动控制。ss是单次执行的时间

​ 从结果上看,两种排序算法的性能相差无几,当然与我们的逻辑太简单也有关系。这次的分享就到这里,大家赶紧用到自己的项目中,测试一下吧。

更准确的测试Java程序性能——JMH基准测试的更多相关文章

  1. Java程序性能定位工具-火焰图

    Java程序性能定位工具-火焰图 前言 Java火焰图是一种新的查看CPU利用率方式.今天就带大家一起使用来自Google大神的工具来生成火焰图.火焰图非常的直观,问题一目了然,希望有一天它能成为JA ...

  2. Java程序性能优化技巧

    Java程序性能优化技巧 多线程.集合.网络编程.内存优化.缓冲..spring.设计模式.软件工程.编程思想 1.生成对象时,合理分配空间和大小new ArrayList(100); 2.优化for ...

  3. 《Java程序性能优化:让你的Java程序更快、更稳定》

    Java程序性能优化:让你的Java程序更快.更稳定, 卓越网更便宜,不错的书吧

  4. [JAVA] java程序性能优化

    一.避免在循环条件中使用复杂表达式 在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快. 例子: import java.util ...

  5. Java程序性能优化——让你的java程序更快、更稳定

    1.Java性能调优概述 1.1.Web服务器,响应时间.吞吐量是两个重要的性能参数. 1.2.程序性能的几个表现: 执行速度:程序的反映是否迅速,响应时间是否足够短 内存分配:分配是否合理,是否过多 ...

  6. Java程序性能优化读书笔记(一):Java性能调优概述

    程序性能的主要表现点: 执行速度:程序的反映是否迅速,响应时间是否足够短 内存分配:内存分配是否合理,是否过多地消耗内存或者存在内存泄漏 启动时间:程序从运行到可以正常处理业务需要花费多少时间 负载承 ...

  7. Java程序性能优化之性能概述

    性能的基本概念 一).什么叫程序的性能? 程序运行所需的内存和时间. 二).性能的表现形式: 1).执行速度: 程序的反应是否迅速,响应时间是否足够短. 2).启动时间:程序从运行到可以处理正常业务所 ...

  8. java程序性能优化

    一.避免在循环条件中使用复杂表达式 在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快. 例子: import java.util ...

  9. 《Java程序性能优化》学习笔记 设计优化

    豆瓣读书:http://book.douban.com/subject/19969386/ 第一章 Java性能调优概述 1.性能的参考指标 执行时间: CPU时间: 内存分配: 磁盘吞吐量: 网络吞 ...

随机推荐

  1. 十七:使用JDBC处理MySQL大数据

    一.基本概念 大数据也称之为LOB(Large Objects),LOB又分为:clob和blob,clob用于存储大文本,blob用于存储二进制数据,例如图像.声音.二进制文等. 在实际开发中,有时 ...

  2. rocketmq知识点

    消息队列mq 参考资料:https://www.jianshu.com/p/824066d70da8 一.消息中间件的主要作用和功能: 1)异步解耦和分流: 2)挡住前端的数据洪峰,保证后端系统的稳定 ...

  3. 【REST】使用RestSharp 库消费Restful Service

    使用RestSharp 库消费Restful Service   现在互联网上的服务接口都是Restful的,SOAP的Service已经不是主流..NET/Mono下如何消费Restful Serv ...

  4. Spring Data JPA:解析JpaSpecificationExecutor & Specification

    源码 在前面关于SimpleJpaRepository的文章[地址]中可以得知,SimpleJpaRepository间接实现了JpaSpecificationExecutor接口,本文就详细探究一下 ...

  5. 跟我一起用unity做小地图!

    lol的小地图 转载爬虫请自重,未问先转没排面 不爱多做铺垫,小地图对于一些游戏来说多重要大家都懂,不然你也不会来看我这篇文章的,对不对? 话不多说,开搞! 一.主体功能 一般来说,游戏里的迷你地图都 ...

  6. GUI常用监听事件

    概念 对鼠标.键盘等一系列事件做出相应的反馈 事件监听 //创建监听事件 public class Demo { public static void main(String[] args) { Fr ...

  7. 被面试官问懵:TCP 四次挥手收到乱序的 FIN 包会如何处理?

    摘要:收到个读者的问题,他在面试的时候,被搞懵了,因为面试官问了他这么一个网络问题. 本文分享自华为云社区<TCP 四次挥手收到乱序的 FIN 包会如何处理?>,作者:小林coding . ...

  8. github上使用C语言实现的线程池

    网上介绍线程池的知识很多,但是在代码实现上介绍的又不是那么多.而且给人的一种感觉就是:你的这种实现是正规的方式还是你自己的实现? 如果有这么个疑问,且想找一个靠谱的代码拿来使用,那么这个项目是个不错的 ...

  9. Postman 根据nginx日志查账号

    1) GET:http://fwm.le-yao.com/api/backend/profile 2) Headers中,在KEY中添加 Content-Type ,对应的VALUE为 applica ...

  10. Hash值和位运算

    一.Hash 1.md5是hash算法,不可逆,还原的是暴力穷举的方式解析的:加盐之后穷举也不能还原: 2.压缩映射会有重复,即哈希冲突: 二.ConcurrentHashMap 1.putIfAbs ...