Java JMH 学习 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
barcke
V2EX    Java

Java JMH 学习

  •  
  •   barcke
    barcke 2024-05-07 17:22:49 +08:00 1581 次点击
    这是一个创建于 596 天前的主题,其中的信息可能已经有所发展或是发生改变。
    div class="markdown_body">

    JMH 是什么?

    JMH ( Java Microbenchmark Harness )是一款专用于代码微基准测试的工具集,其主要聚焦于方法层面的基准测试,精度可达纳秒级别。此工具由 Oracle 内部负责实现 JIT 的杰出人士编写,他们对 JIT 及 JVM 在基准测试方面的影响有着深刻的理解。JMH 不仅能够对 Java 语言进行基准测试,还能对运行在 JVM 上的其他语言开展基准测试。 当热点方法被确定,且希望进一步提升方法性能时,可借助 JMH 对优化后的结果进行量化分析。 JMH 具有一些典型的应用场景,如:

    • 精准了解某个方法的执行时间,以及执行时间与输入的相关性;
    • 对比接口不同实现方式在特定条件下的吞吐量;
    • 查看在特定时间段内完成的请求所占比例等。

    JMH 在 JDK9 中是自带的 在 JDK9 之前 我们需要主动的引入下 maven 包

    <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.23</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.23</version> </dependency> 

    让我们来些第一个 JMH 测试代码吧!

    package cn.ideamake.im.auth.service; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.results.format.ResultFormatType; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import com.google.common.collect.Lists; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; /** * @author Barcke * @version 1.0 * @projectName im-auth * @className Test * @date 2024/4/30 16:30 * @slogan: 源于生活 高于生活 * @description: **/ // 用来配置 Mode 选项,可用于类或者方法上,这个注解的 value 是一个数组,可以把几种 Mode 集合在一起执行 比如 @BenchmarkMode({Mode.AverageTime, Mode.SampleTime}) @BenchmarkMode( // 每一次方法执行用的平均时间,每次操作的平均时间,单位为 time/op // Mode.AverageTime // 随机取样,最后输出取样结果的分布 // Mode.SampleTime // 只运行一次,往往同时把 Warmup 次数设为 0 ,用于测试冷启动时的性能 // Mode.SingleShotTime // 吞吐量,每秒执行了多少次调用,单位为 ops/time Mode.Throughput // 上面的所有模式都执行一次 // Mode.All ) // 预热所需要配置的一些基本测试参数,可用于类或者方法上。一般前几次进行程序测试的时候都会比较慢,所以要让程序进行几轮预热,保证测试的准确性。 @Warmup( // 预热次数 iteratiOns= 3 // 每次预热的时间 , time = 1 // 时间单位,默认秒 , timeUnit = TimeUnit.SECONDS // 批处理大小,每次操作调用几次方法 , batchSize = 100 ) @Measurement(iteratiOns= 5, time = 5) // 每个测试线程数量,可用于类或者方法上。 @Threads(5) // 进行 fork 的次数,可用于类或者方法上。如果 fork 数是 3 的话,则 JMH 会 fork 出三个进程来进行测试。 @Fork(3) // 指定一个对象的作用范围,JMH 根据 scope 来进行实例化和共享操作。 @State 可以被继承使用,如果父类定义了该注解,子类则无需定义。由于 JMH 允许多线程同时执行测试 @State( // 默认是 Scope.Benchmark 所有测试线程共享一个实例,测试有状态实例在多线程共享下的性能 // value = Scope.Benchmark // 线程作用域,每个线程都有自己的实例,每个线程的实例相互独立,不会相互影响 value = Scope.Thread // 线程组作用域,线程组内的线程共享一个实例,线程组外的线程不会共享实例,每个线程的实例相互独立,不会相互影响 // value = Scope.Group ) // 结果输出时间单位 可用于类或者方法注解 @OutputTimeUnit(TimeUnit.NANOSECONDS) public class Test { private final String KEY = "barcke"; private List<String> arrayList; private List<String> linkedList; // 测试参数 @Param(value = {"10", "20", "30"}) private int length; // @Setup 测试初始化方法 对应在执行测试方法之后调用的可以使用注解 @TearDown 声明 @Setup( // 由于我们设置 Warmup 和 Measurement 的不同,因此每一个基准测试方法都会被执行若干个批次,如果想要在每一个基准测试批次执行的前后调用方法,则可以将 Level 设置为 Iteration 。 // Level.Iteration // 意味着在每一个批次的度量过程中,每一次对基准方法的调用前都会执行套件方法。 // Level.Invocation // Setup 和 TearDown 默认的配置,该套件方法会在每一个基准测试方法的所有批次执行的前后被执行。 Level.Trial ) public void setup() { arrayList = Lists.newCopyOnWriteArrayList(); linkedList = new LinkedList<>(); } // Benchmark 用于 测试方法声明 @Benchmark public List<String> arrayListAdd() { for (int i = 0; i < length; i++) { arrayList.add(KEY); } return arrayList; } // Benchmark 用于 测试方法声明 @Benchmark public List<String> linkedListAdd() { for (int i = 0; i < length; i++) { linkedList.add(KEY); } return linkedList; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() // 要导入的测试类 .include(Test.class.getSimpleName()) // 输出测试结果的文件 .result("result.json") .resultFormat(ResultFormatType.JSON).build(); new Runner(opt).run(); } } 

    需要进行测试的方法会用 @Benchmark 注解来标识,关于这些注解的详细含义,将在后续内容中进行具体阐述。 在 main()函数中,首先会对测试用例进行相应的配置。通过采用 Builder 模式来配置测试,将配置的各项参数存入 Options 对象中,然后再利用该 Options 对象来构建 Runner 并启动测试。

    使用 Main 方法执行测试

    需注意!!! idea 执行基准测试过程中可能会出现内存泄露的报错:java.lang.OutOfMemoryError: Java heap space 加大 JVM 的内存参数值即可 如: image image

    做好准备工作后,开始运行代码,静待片刻,测试结果就会出炉,下面对结果做一个简单的分析: image

    image

    image 最终结果: image

    可视化 JMH 工具

    JMH Visual Chart: http://deepoove.com/jmh-visual-chart/ JMH Visualizer: https://jmh.morethan.io/

    将 json 文件导入到网站中则可以得到可视化图表数据 image

    本文到此结束,希望对你有帮助~

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1524 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 19ms UTC 16:33 PVG 00:33 LAX 08:33 JFK 11:33
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86