当前位置: 首页 > 后端技术 > Java

Java性能调优必备工具——JMH

时间:2023-04-01 13:54:52 Java

iffast还是switchfast?是否应该指定HashMap的初始化大小,指定后性能能提升多少?各种序列化方法中哪种方法花费的时间更少?无论出于何种原因进行绩效评估,量化指标始终是必要的。在大多数情况下,简单地回答谁快谁慢是不够的。如何量化程序性能?这就需要我们的主角JMH出场了!JMH简介JMH(JavaMicrobenchmarkHarness)是一个代码微基准测试工具套件,主要基于方法级基准测试,精度可达纳秒级。这个工具是由Oracle内部JIT实现背后的人编写的,他们应该比任何人都更了解JIT和JVM对基准测试的影响。当你定位到一个热点方法,想进一步优化该方法的性能时,可以使用JMH对优化结果进行定量分析。JMH的典型应用场景如下:想知道一个方法到底执行了多长时间,以及执行时间和输入的相关性比较不同接口实现在给定条件下的吞吐量查看多少请求占多少百分比好久才完成下面我们以字符串拼接的两种方式为例,使用JMH做benchmark测试。添加依赖,因为JMH是JDK9自带的。如果是JDK9之前的版本,需要添加如下依赖(目前JMH最新版本为1.23):org.openjdk.jmhjmh-core1.23org.openjdk.jmhjmh-generator-annprocess1.23写一个benchmark测试接下来创建一个JMH测试类,判断+和StringBuilder.append()哪个字符串拼接耗时少。具体代码如下:AverageTime)@Warmup(iterations=3,time=1)@Measurement(iterations=5,time=5)@Threads(4)@Fork(1)@State(value=Scope.Benchmark)@OutputTimeUnit(TimeUnit.NANOSECONDS)publicclassStringConnectTest{@Param(value={"10","50","100"})privateintlength;@BenchmarkpublicvoidtestStringAdd(Blackholeblackhole){Stringa="";for(inti=0;iorg.apache.maven.pluginsmaven-shade-plugin2.4.1packageshadejmh-demoorg.openjdk.jmh.Main/插头在>然后执行maven命令生成可执行jar包,执行:mvncleaninstalljava-jartarget/jmh-demo.jarStringConnectTestJMH基础知识为了更好的使用JMH的功能,下面对JMH进行说明基本概念:@BenchmarkMode用于配置Mode选项,可以用在类或方法上。这个注解的值是一个数组,可以用来同时执行几个Mode,比如:@BenchmarkMode({Mode.SampleTime,Mode.AverageTime}),也可以设置为Mode.All,即执行itallonceThroughput:整体吞吐量,每秒执行多少次调用,以ops/timeAverageTime:平均使用时间,每次操作的平均时间,以时间为单位/opSampleTime:随机采样,最后输出采样结果的分布SingleShotTime:只运行一次,经常同时设置Warmup次数为0,用于测试冷启动性能All:将以上所有模式执行一次@StatePassState可以指定一个对象的范围,java训练JMH根据范围实例化和共享操作。@State可以被继承和使用。如果父类定义了注解,则子类不需要定义。由于JMH允许多个线程同时执行测试,所以不同选项的含义如下:Scope.Benchmark:所有测试线程共享一个实例,测试多线程共享下有状态实例的性能Scope.Group:该同一个线程在同一个组中共享InstanceScope.Thread:默认状态,每个测试线程分配一个实例@OutputTimeUnit作为统计结果的时间单位,可以用于类或者方法注解@Warmup的一些基本测试参数需要配置预热可用于类或方法。一般前几次程序测试会比较慢,所以让程序进行几轮预热,以保证测试的准确性。参数如下:iterations:预热次数time:每次预热的时间timeUnit:时间单位,默认秒batchSize:批量大小,每次操作调用多少次方法为什么需要热身?由于JVM的JIT机制,如果一个函数被多次调用,JVM会尝试将其编译成机器码来提高执行速度,所以为了让benchmark结果更接近真实情况,需要被热身。@Measurement一些基本的测试参数需要配置才能真正调用方法,可以用在类或者方法上,参数和@Warmup一样。@Threads每个进程中的测试线程,可以用在类或方法上。@Forkfork次数,可以用在类或方法上。如果fork数为2,JMH会fork两个进程进行测试。@Param指定了某个参数的多种情况,特别适用于测试一个函数在不同参数输入条件下的性能。它只能用于字段。要使用此注释,您必须定义@State注释。介绍完常用的注解,再看看JMH都有哪些坑。JMH陷阱使用JMH时需要避免一些陷阱。比如JIT优化中的死代码剔除,比如下面的代码:@BenchmarkpublicvoidtestStringAdd(Blackholeblackhole){Stringa="";for(inti=0;iSettings...->Plugins,然后搜索jmh,选择安装JMH插件:JMHplugin这个插件可以让我们像JUnit一样使用JMH,主要功能如下:自动生成带有@Benchmark的方法和JUnit一样,运行一个单独的Benchmark方法RunallBenchmarkmethodsintheclass例如可以右键Generate...,选择操作GenerateJMHbenchmark可以生成带有@Benchmark的方法。还有一个Benchmark方法,它通过将光标移动到方法声明并调用Run操作来运行。将光标移动到类名所在行,右键Run运行,该类下所有带有@Benchmark注解的方法都会执行。JMH可视化,比如导入上述测试示例结果的json文件,可以实现可视化:总结本文主要介绍性能基准测试工具JMH,通过一些功能可以避免JIT或者JVM中的其他优化对性能测试的影响.只需用@Benchmark注解标注需要测试的业务逻辑,JMH的注解处理器就可以自动生成真正的性能测试代码和对应的性能测试配置文件。