关于java:JMH

3次阅读

共计 4581 个字符,预计需要花费 12 分钟才能阅读完成。

JMH 是什么
JMH 是 Java Microbenchmark Harness 的简称,一个针对 Java 做基准测试的工具,是由开发 JVM 的那群人开发的。想精确的对一段代码做基准性能测试并不容易,因为 JVM 层面在编译期、运行时对代码做很多优化,然而当代码块处于整个零碎中运行时这些优化并不一定会失效,从而产生谬误的基准测试后果,而这个问题就是 JMH 要解决的。

JMH vs JMeter
JMeter 可能是最罕用的性能测试工具。它既反对图形界面,也反对命令行,属于黑盒测试的领域,对非开发人员比拟敌对,上手也非常容易。图形界面个别用于编写、调试测试用例,而理论的性能测试倡议还是在命令行下运行。

很多场景下 JMeter 和 JMH 都能够做性能测试,然而对于严格意义上的基准测试来说,只有 JMH 才适宜。JMeter 的测试后果精度绝对 JVM 较低、所以 JMeter 不适宜于类级别的基准测试,更适宜于对精度要求不高、耗时绝对较长的操作。

JMeter 测试精度差:JMeter 本身框架比拟重,举个例子:应用 JMH 测试一个办法,均匀耗时 0.01ms,而应用 JMeter 测试的后果均匀耗时 20ms,相差 200 倍。
JMeter 内置很多采样器:JMeter 内置了反对多种网络协议的采样器,能够在不写 Java 代码的状况下实现很多简单的测试。JMeter 反对集群的形式运行,不便模仿多用户、高并发压力测试。
总结:JMeter 适宜一些绝对耗时的集成功能测试,如 API 接口的测试。JMH 适宜于类或者办法的单元测试。

JMH 根本用法
创立 JMH 我的项目
官网举荐为 JMH 基准测试创立独自的我的项目,最简略的创立 JMH 我的项目的办法就是基于 maven 我的项目原型的形式创立(如果是在 windows 环境下,须要对 org.open.jdk.jmh 这样带. 的用双引号包裹)。

复制代码
mvn archetype:generate

      -DinteractiveMode=false
      -DarchetypeGroupId=org.openjdk.jmh
      -DarchetypeArtifactId=jmh-java-benchmark-archetype
      -DarchetypeVersion=1.21
      -DgroupId=com.jenkov
      -DartifactId=first-benchmark
      -Dversion=1.0

复制代码
能够看到生成的我的项目 pom 文件中次要是增加了两个 jmh
的依赖和设置了 maven-shade-plugin 的编译形式(负责把我的项目的所有依赖 jar 包打入到指标 jar 包中,与 springboot 的实现形式相似)。
复制代码
<dependencies>

    <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>
</dependencies>


<plugin>

<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.2</version>
<executions>
    <execution>
        <phase>package</phase>
        <goals>
            <goal>shade</goal>
        </goals>
        <configuration>
            <finalName>${uberjar.name}</finalName>
            <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                    <mainClass>org.openjdk.jmh.Main</mainClass>
                </transformer>
            </transformers>
            <filters>
                <filter>
                    <!--
                        Shading signed JARs will fail without this.
                        http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
                    -->
                    <artifact>*:*</artifact>
                    <excludes>
                        <exclude>META-INF/*.SF</exclude>
                        <exclude>META-INF/*.DSA</exclude>
                        <exclude>META-INF/*.RSA</exclude>
                    </excludes>
                </filter>
            </filters>
        </configuration>
    </execution>
</executions>

</plugin>
复制代码
生成的我的项目中曾经蕴含了一个 class 文件 MyBenchmark.java,如下:

复制代码
public class MyBenchmark {

@Benchmark
public void testMethod() {
    // This is a demo/sample template for building your JMH benchmarks. Edit as needed.
    // Put your benchmark code here.
}

}
复制代码
编写基准测试代码
在下面生成的 MyBenchmark 类的 testMethod 中就能够增加基准测试的 java 代码,举例如下:测试 AtomicInteger 的 incrementAndGet 的基准性能。

复制代码
public class MyBenchmark {

static AtomicInteger integer = new AtomicInteger();

@Benchmark
public void testMethod() {
    // This is a demo/sample template for building your JMH benchmarks. Edit as needed.
    // Put your benchmark code here.
    integer.incrementAndGet();}

}
复制代码
JMH 打包、运行
我的项目打包

mvn clean install
运行生成的指标 jar 包 benchmark.jar:

复制代码
java -jar benchmark.jar

JMH version: 1.21

VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13

VM invoker: C:\Java\jdk1.8.0_181\jre\bin\java.exe

VM options: <none>

Warmup: 5 iterations, 10 s each

Measurement: 5 iterations, 10 s each

Timeout: 10 min per iteration

Threads: 1 thread, will synchronize iterations

Benchmark mode: Throughput, ops/time

Benchmark: org.sample.MyBenchmark.testMethod

Run progress: 0.00% complete, ETA 00:01:40

Fork: 1 of 1

Warmup Iteration 1: 81052462.185 ops/s

Warmup Iteration 2: 80152956.333 ops/s

Warmup Iteration 3: 81305026.522 ops/s

Warmup Iteration 4: 81740215.227 ops/s

Warmup Iteration 5: 82398485.097 ops/s

Iteration 1: 82176523.804 ops/s
Iteration 2: 81818881.730 ops/s
Iteration 3: 82812749.807 ops/s
Iteration 4: 82406672.531 ops/s
Iteration 5: 74270344.512 ops/s

Result “org.sample.MyBenchmark.testMethod”:
80697034.477 ±(99.9%) 13903555.960 ops/s [Average]
(min, avg, max) = (74270344.512, 80697034.477, 82812749.807), stdev = 3610709.330
CI (99.9%): [66793478.517, 94600590.437] (assumes normal distribution)

Run complete. Total time: 00:01:41

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark Mode Cnt Score Error Units
MyBenchmark.testMethod thrpt 5 80697034.477 ± 13903555.960 ops/s
复制代码
从下面的日志咱们大抵能够理解到 JMH 的基准测试次要经验了上面几个过程:

打印本次测试的配置,warmup:5 轮;measurement:5 轮;每轮:10s;启动 1 个线程做测试;基准测试指标:吞吐量(throughput,单位是 s);测试方法 MyBenchmark.testMethod
启动一个 JVM 过程做基准测试(也能够设置启动多个过程,缩小随机因素的误差影响)
在 JVM 过程中先执行了 5 轮的预热(warmup),每轮 10s,总共 50s 的预热工夫。预热的数据不作为基准测试的参考。
测试了 5 轮,每轮 10s,总共 50s 的测试工夫
汇总测试数据、生成后果报表。最终论断是吞吐量(80697034.477 ±13903555.960 ops/s), 其中 80697034.477 是后果,13903555.960 是误差范畴。

正文完
 0