Java性能优化-掌握JMH

作者:闲大赋链接:https://my.oschina.net/xiandafu/blog/3067186关于JMH,可以直接查看官网地址http://openjdk.JAVA.net/projects/code-tools/jmh/
1.3 JMH
1.3.1 使用JMH
通过手工编写一个性能压测程序有较多的问题

  • 不同需要性能比较方法放到一个虚拟机里调用,有可能会互相影响 。最好的办法是分成俩个独立的进程运行,确保俩个对比方法不相互影响 。
  • PerformaceAreaTest启动后直接运行,缺少预热代过程 。虚拟机在执行代码过程中,会加载类,解释执行,以及有可能的优化编译 。需要确保虚拟机进行了一定预热运行,以保证测试的公平性,我们在运行PerformaceAreaTest2的时候,能看到第一次循环执行时间总是较长 。可以参考第8章了解JIT
  • 为了避免环境影响造成的对结果统计不准,我们需要运行多次,取出平均成绩
  • 需要从多个纬度统计方法的性能,统计冷启动需要消耗的时间,统计OPS,TP99的功能 。
JMH使用OPS来表示吞吐量,OPS,Opeartion Per Second,是衡量性能的重要指标,指得是每秒操作量 。数值越大,性能越好 。类似的概念还有TPS,表示每秒的事务完成量,QPS,每秒的查询量 。如果对每次执行时间进行升序排序,取出总数的99%的最大执行时间作为TP99的值,TP99通常是衡量系统性能重要指标,他表示99%的请求的响应时间不超过某个值 。比TP99更严格的事TP999,要求99.9%的请求不超过某个值
有什么工具能帮助我们统计性能优化后的效果,比如更方便的统计OPS,TP99等 。同时,我们为了做调优,不必每次都自己写一个测试程序
JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件 。主要是基于方法层面的基准测试,精度可以达到纳秒级 。当你定位到热点方法,希望进一步优化方法性能的时候,就可以使用JMH对优化的结果进行量化的分析 。
JMH 实现了JSR269规范,即注解处理器,能在编译Java源码的时候,识别的到需要处理的注解,如@Beanmark,JMH能根据@Beanmark的配置生成一系列测试辅助类 。关于JSR269,本书11章详细介绍. 流行开源Lombok 基于JSR269规范
开始是使用JMH,可以在工程里添加对JMH的依赖,添加如下
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
${jmh.version} 为jmh最新版本,为1.0
我们编写一个JMH测试类
@BenchmarkMode(Mode.Throughput)@Warmup(iterations = 3)@Measurement(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)@Threads(1)@Fork(1)@OutputTimeUnit(TimeUnit.SECONDS)public class MyBenchmark {@Benchmark public static void testStringKey(){ //优化前的代码 } @Benchmark public static void testObjectKey(){ //要测试的优化后代码 } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(MyBenchmark.class.getSimpleName()) .build(); new Runner(opt).run(); }}MyBenchmark 有俩个需要比较的方法,都用 @Benchmark注解标识,MyBenchmark用了一系列注解,解释如下
  • BenchmarkMode,使用模式,默认是Mode.Throughput,表示吞吐量,其他参数还有AverageTime,表示每次执行时间,SampleTime表示采样时间,SingleShotTime表示只运行一次,用于测试冷启动消耗时间,All表示统计前面的所有指标
  • Warmup 配置预热次数,默认是每次运行1秒,运行10次,我们的例子是运行3次
  • Measurement 配置执行次数,本例是一次运行5秒,总共运行3次 。在性能对比时候,采用默认1秒即可,如果我们用jvisualvm做性能监控,我们可以指定一个较长时间运行 。
  • Threads 配置同时起多少个线程执行,默认值世 Runtime.getRuntime().availableProcessors(),本例启动1个线程同时执行
  • Fork,代表启动多个单独的进程分别测试每个方法,我们这里指定为每个方法启动一个进程 。
  • OutputTimeUnit 统计结果的时间单元,这个例子TimeUnit.SECONDS,我们在运行后会看到输出结果是统计每秒的吞吐量
我们在MyBenchmark添加需要的测试方法,如下
static AreaService areaService = new AreaService();static PreferAreaService perferAreaService = new PreferAreaService();static List<Area> data = https://www.isolves.com/it/cxkf/yy/JAVA/2019-08-27/buildData(20);@Benchmarkpublic static void testStringKey(){ areaService.buildArea(data);}@Benchmarkpublic static void testObjectKey(){ perferAreaService.buildArea(data);}private static List


推荐阅读