详解Java使用JMH进行基准性能测试

目录
  • 一、前言
  • 二、JMH概述
    • 1、什么是JMH
    • 2、JMH适用的典型场景
    • 3、JMH基本概念
  • 三、JMH的使用
    • 1、快速跑起来
    • 2、JMH常用注解详细介绍
  • 四、小结
  • 附:

一、前言

在日常开发工作当中,开发人员可能有这些困惑:自己写的这个方法性能到底怎么样?在原接口实现方法中添加了新的业务逻辑,对整个接口的性能影响有多少?有多种实现方式(或开源类库),到底哪一种性能更好?…
当遇到类似困惑或者说问题的时候,怎么办呢?当然是测试验证,实践出真知!本文讲述的就是一个方法级别的性能测试工具——JMH。

二、JMH概述

1、什么是JMH

  JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件。何谓Micro Benchmark呢?简单的来说就是基于方法层面的基准测试,精度可以达到微秒级。其由Oracle/openjdk内部开发JIT编译器的大佬们所开发,作为java的方法级性能测试工具可以说是根正苗红了。(官方地址:http://hg.openjdk.java.net/code-tools/jmh/

2、JMH适用的典型场景

  a、优化热点方法,准确的知道某个方法的执行耗时,以及不同入参与最终实际耗时的关系,从而针对性的进行优化;
  b、寻找最佳方案,验证接口方法不同实现方式的实际吞吐量,从而确定最佳实现方式 。如:选择json转换工具时选fastjson还是gson、字符串连接使用StringBuilder方式还是直接相加;
  c、分析性能损耗,在原接口方法业务逻辑中添加新的业务代码时,对整个业务方法的性能影响。如:在原业务逻辑中,添加一个插入操作日志的操作,可以分析新加操作对整个业务方法的性能影响。
  d、分析百分比内的耗时,即测试方法多次调用时百分比区间内的耗时,如:测试调用某个方法,50%以内的调用耗时是8.2ms/op,90%以内是9.3ms/op,99.99%以内是10.2ms/op,等等。(模式为Mode.SampleTime)

3、JMH基本概念

  a、Mode :表示JMH测试中的模式,默认有5种,分别是Throughput(吞吐量)、AverageTime(平均耗时)、SampleTime(随机采样)、SingleShotTime(单次执行)、All(以上4种都来一次);
  b、Fork:表示JMH将用来测试的进程数;
  c、Warmup : 表示预热,在HotSpot中,JVM的JIT编译器会对热点代码进行编译优化, 因此为了最接近真实的情况,需要先预热测试代码,使JIT编译器完成可能需要的优化,从而令JMH最终测试结果更加准确;
  d、Iteration :表示JMH中的最小测试迭代单位,即测试次数,一般默认值是每次1s;
  e、Benchmark:用于标注JMH将进行测试的方法。(类似Junit中的@Test注解)

三、JMH的使用

1、快速跑起来

  JMH的基本使用只需2步,第1步是引入maven依赖包,第2步是根据工具框架模板编写测试类,以下通过一个简单例子进行详细说明:

例1:测试一个方法的平均耗时

第1步:引入maven依赖: (笔者使用的jmh版本为1.21)

<!-- 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>

第2步:编写测试方法:

package com.xiaojiang;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

/**
 * @ Description:jmh使用第一个例子
 * @ Author     :xiaojiang
 * @ Date       :Created in 2019-06-19
 * @ Version    :0.0.1
 */
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
public class JmhDemoOne {
    public static void main(String[] args) throws Exception{
        Options options = new OptionsBuilder()
                .include(JmhDemoOne.class.getName())
                .build();
        new Runner(options).run();
    }
    /**
     * 测试sayHello的平局耗时
     *
     * @throws Exception
     */
    @Benchmark
    public void sayHello() throws Exception{
        //TODO 业务方法 ,此处用休眠的方式模拟业务耗时10 ms
        TimeUnit.MILLISECONDS.sleep(10);
    }
}

代码说明:

通过以上例子可以发现,一个基本的JMH测试实现其实并不是很复杂,非常类似于用Junit做单元测试。具体说明如下:
  a、类名JmhDemoOne 上的@OutputTimeUnit、@BenchmarkMode这两个注解,表明这是一个JMH的测试类;(具体注解含义 ,以及更多注解说明请参考下文JMH常用注解详细介绍)
  b、主函数入口main方法中指定了一些基本测试参数选项;(基本就是固定写法。其实有更多相关参数方法可以添加,但这些参数笔者建议通过注解的方式在类上直接添加,这样来的更加方便)
  c、通过@Benchmark注解标注需要benchmark(基准测试)的具体方法;

直接运行测试方法,控制台输出测试结果如下:(笔者JDK版本为1.8,IDE工具为IDEA2018)

# JMH version: 1.21
# VM version: JDK 1.8.0_144, Java HotSpot(TM) 64-Bit Server VM, 25.144-b01
# VM invoker: D:\Java\jdk1.8.0_144\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.5\lib\idea_rt.jar=55987:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.5\bin -Dfile.encoding=UTF-8
# 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: Average time, time/op
# Benchmark: com.xiaojiang.JmhDemoOne.sayHello

# Run progress: 0.00% complete, ETA 00:08:20
# Fork: 1 of 5
# Warmup Iteration   1: 10.716 ms/op
# Warmup Iteration   2: 10.640 ms/op
# Warmup Iteration   3: 10.737 ms/op
# Warmup Iteration   4: 10.693 ms/op
# Warmup Iteration   5: 10.723 ms/op
Iteration   1: 10.716 ms/op
Iteration   2: 10.724 ms/op
Iteration   3: 10.772 ms/op
Iteration   4: 10.758 ms/op
Iteration   5: 10.709 ms/op

# Run progress: 20.00% complete, ETA 00:06:43
# Fork: 2 of 5
# Warmup Iteration   1: 10.744 ms/op
# Warmup Iteration   2: 10.732 ms/op
# Warmup Iteration   3: 10.748 ms/op
# Warmup Iteration   4: 10.728 ms/op
# Warmup Iteration   5: 10.760 ms/op
Iteration   1: 10.701 ms/op
Iteration   2: 10.709 ms/op
Iteration   3: 10.719 ms/op
Iteration   4: 10.714 ms/op
Iteration   5: 10.703 ms/op

# Run progress: 40.00% complete, ETA 00:05:02
# Fork: 3 of 5
# Warmup Iteration   1: 10.729 ms/op
# Warmup Iteration   2: 10.731 ms/op
# Warmup Iteration   3: 10.728 ms/op
# Warmup Iteration   4: 10.700 ms/op
# Warmup Iteration   5: 10.709 ms/op
Iteration   1: 10.708 ms/op
Iteration   2: 10.701 ms/op
Iteration   3: 10.708 ms/op
Iteration   4: 10.726 ms/op
Iteration   5: 10.698 ms/op

# Run progress: 60.00% complete, ETA 00:03:21
# Fork: 4 of 5
# Warmup Iteration   1: 10.724 ms/op
# Warmup Iteration   2: 10.688 ms/op
# Warmup Iteration   3: 10.748 ms/op
# Warmup Iteration   4: 10.732 ms/op
# Warmup Iteration   5: 10.772 ms/op
Iteration   1: 10.729 ms/op
Iteration   2: 10.688 ms/op
Iteration   3: 10.705 ms/op
Iteration   4: 10.687 ms/op
Iteration   5: 10.709 ms/op

# Run progress: 80.00% complete, ETA 00:01:40
# Fork: 5 of 5
# Warmup Iteration   1: 10.688 ms/op
# Warmup Iteration   2: 10.696 ms/op
# Warmup Iteration   3: 10.692 ms/op
# Warmup Iteration   4: 10.684 ms/op
# Warmup Iteration   5: 10.683 ms/op
Iteration   1: 10.719 ms/op
Iteration   2: 10.720 ms/op
Iteration   3: 10.695 ms/op
Iteration   4: 10.710 ms/op
Iteration   5: 10.760 ms/op

Result "com.xiaojiang.JmhDemoOne.sayHello":
  10.716 ±(99.9%) 0.016 ms/op [Average]
  (min, avg, max) = (10.687, 10.716, 10.772), stdev = 0.021
  CI (99.9%): [10.700, 10.731] (assumes normal distribution)

# Run complete. Total time: 00:08:24

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
JmhDemoOne.sayHello  avgt   25  10.716 ± 0.016  ms/op

Process finished with exit code 0

测试结果说明:
整个测试结果分为3大块,测试基本参数信息、测试过程、测试结果,各行含义具体说明如下:

说明
参数信息(1-10行) 1:jmh版本
2:jvm版本信息
3:jvm程序(jdk安装路径)
4:jvm参数配置
5:预热参数:预热次数、每次持续时间
6:测试参数:测试次数、每次持续时间
7:每次测试迭代超时时间
8:每个测试进程的测试线程数
9: 测试的模式
10:测试的方法
测试过程(12-75行) 12-23:第1次fork测试 (fork可以理解为1个独立的进程)
12:测试完成进度,预计剩余需要时间
13:当前第几次fork
14-18:预热执行,每次预热执行耗时
19-23:正式测试执行,每次测试执行耗时
25-36:第2次fork测试
38-49:第3次fork测试
51-62:第4次fork测试
64-75:第5次fork测试
测试结果(78-95行) 78-81:测试结果,包括测试的方法、平均耗时[平局耗时的比例]、最大最小 耗时、测试结果数据离散度(stdev)等
84:测试总耗时
86-90:对测试结果的解释
92-93:测试结论{测试的方法、测试类型(Mode)、测试总次数(Cnt)、测试结果(Score)、误差(Error)、单位(Units)}
95:结束

注:
  a、测试结果中的Measurement、Fork、Warmup等参数,是JMH采用了默认的配置值,实际使用中,我们可根据需要指定相关参数。
  b、运行这个测试类可以在IDEA中直接跑,也可以打成 jar 包到服务器上跑。
  c、本测试结果是直接输出在控制台,如有需要,可将测试结果输出到文件中,方法是在options中添加output方法指定测试结果输出目录,如下:

Options options = new OptionsBuilder()
        .include(JmhDemoOne.class.getName())
        .output("D:/JmhDemoOne.log")   //将测试结果输出到指定目录文件
        .build();

2、JMH常用注解详细介绍

注解 介绍
@BenchmarkMode 基准测试模式。一共有5种可选值:(其实是4种)
Mode.Throughput:吞吐量模式,即单位时间内方法的吞吐量
Mode.AverageTime:平均耗时模式,即一定测试次数内方法执行的平均耗时
Mode.SampleTime:随机采样模式,即最终结果为取样结果分布比例
Mode.SingleShotTime:单次执行模式,即只会执行一次(以上的模式通常会有预热、会迭代执行多次,这个模式可用于测试某些特定场景,如冷启动时的性能)
Mode.All:即以上模式都执行一遍

-----------------------------------
用法示例:(benchmark模式为平均耗时模式)
@BenchmarkMode(Mode.AverageTime)

@OutputTimeUnit 测试结果的时间单位。其值为java.util.concurrent.TimeUnit 枚举中的值,通常用的值是秒、毫秒、微妙(需要注意的是,在不同测试模式下,需要选择合适的时间单位,从而获取更精确的测试结果。)

------------------------------------
用法示例:(benchmark结果时间单位为毫秒)
@OutputTimeUnit(TimeUnit.MILLISECONDS)

@Benchmark 基准测试,方法级注解(配置在方法名上)。用于标注需要进行benchmark (基准测试)的方法

------------------------------------
用法示例:(方法需要benchmark)
@Benchmark

@Warmup 预热参数。配置预热的相关参数,参数含义是:iterations(预热次数)、time (预热时间)、timeUnit (时间单位)

------------------------------------
用法示例:(预热10次,每次20s)
@Warmup(iterations = 10, time = 20, timeUnit = TimeUnit.SECONDS)

@Measurement 度量,即benchmark基本参数。参数含义是:iterations(测试次数)、time (每次测试时间)、timeUnit (时间单位)

------------------------------------
用法示例:(测试5次,每次30s)
@Measurement(iterations = 5, time = 30, timeUnit = TimeUnit.SECONDS)

@Fork 分叉,即进程数。用于配置将使用多少个进程进行测试

------------------------------------
用法示例:(使用3个进程)
@Fork(3)

@Threads 线程数。每个Fork(进程)中的线程数,一般可设为测试机器cpu核心数。

------------------------------------
用法示例:(使用4个线程)
@Threads(4)

@Param 成员参数,属性级注解。用于测试方法在不同入参情况下的性能表现。

------------------------------------
用法示例:(入参值依次为1 、10、100)
@Param({“1”, “10”, “100”})

@Setup 设置,方法级注解。用于标注benchmark前的操作,通常用于测试前初始化参数资源,如初始化数据库连接等。

------------------------------------
用法示例:(初始化方法)
@Setup

@TearDown 拆卸,方法级注解。用于标注benchmark后的操作,通常用于测试后回收资源,如关闭数据库连接等。

------------------------------------
用法示例:(回收方法)
@TearDown

@State 状态,表示一个类/方法的可用范围,其值有3个:
Scope.Thread:默认状态,每个线程分配一个独享的实例;
Scope.Benchmark:测试中的所有线程共享实例;(多线程测试情况下)
Scope.Group:同一个组的线程共享实例;

------------------------------------
用法示例:(默认值,每个线程分配一个实例)
@State(Scope.Thread)

@Group 测试组,方法级注解。适用分组测试,每组线程数不一样的场景。

------------------------------------
用法示例:(组名为“group_name”的一个组)
@Group(“group_name”)

@GroupThreads 组线程数,方法级注解。通常和@Group搭配使用

------------------------------------
用法示例:(组线程数为10)
@GroupThreads(10)

@Timeout 超时时间。每次测试迭代超时时间

------------------------------------
用法示例:(每次测试超时时间为20min)
@Timeout(time = 20, timeUnit = TimeUnit.MINUTES)

以上是使用JMH测试中常用的注解,当然JMH还有一些其它注解,如@CompilerControl、@AuxCounters 等等,这些注解通常可用于满足特定的测试场景需求,具体相关使用如有需要,可参考官方示例,官方demo比较详细,比较好理解学习。

3、更多示例

本小节笔者将通过几个小示例,展示JMH的基本使用。
例2:
测试验证字符串连接处理时,使用StringBuilder方式是否比直接相加好。

package com.xiaojiang;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
 * @ Description:jmh使用第二个例子
 * @ Author     :xiaojiang
 * @ Date       :Created in 2019-06-19
 * @ Version    :0.0.1
 */
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
public class JmhDemoTwo {
    public static void main(String[] args) throws Exception{
        Options options = new OptionsBuilder()
                .include(JmhDemoTwo.class.getName())
                .build();
        new Runner(options).run();
    }
    /**
     * 字符串个数
     */
    @Param({"10", "100", "1000"})
    private int number;
    /**
     * 字符串直接相加方式
     */
    @Benchmark
    public void StringAddMode(){
        String str = "";
        for(int i=0;i<number;i++){
            str = str + i;
        }
    }
    /**
     * 字符串通过StringBuilder的append方式
     */
    @Benchmark
    public void StringBuilderMode(){
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<number;i++){
            sb.append(i);
        }
    }
}

测试结果:

//---省略测试过程结果----------
Benchmark                     (number)   Mode  Cnt         Score        Error  Units
JmhDemoTwo.StringAddMode            10  thrpt   50   7670608.558 ±  99068.181  ops/s
JmhDemoTwo.StringAddMode           100  thrpt   50    437133.436 ±   7738.031  ops/s
JmhDemoTwo.StringAddMode          1000  thrpt   50      4023.846 ±     62.872  ops/s
JmhDemoTwo.StringBuilderMode        10  thrpt   50  22608867.036 ± 669332.843  ops/s
JmhDemoTwo.StringBuilderMode       100  thrpt   50   1232847.661 ±  23742.088  ops/s
JmhDemoTwo.StringBuilderMode      1000  thrpt   50     98367.745 ±   1487.840  ops/s

从测试结果可以看出,在字符串连接数量分别为10、100、1000时,通过StringBuilder处理字符串的方式比直接相加的方式性能都要强一些;如,当字符窜数量为1000时,直接相加方式的方法吞吐量为4023.846 ops/s,StringBuilder的方式方法吞吐量达到 98367.745ops/s 。(当然具体测试结果值和机器配置、JVM配置有关)

例3:
测试常用序列化json库fastJson、gson、jackson的性能(均为截止2019.06最新版本)。

package com.xiaojiang;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * @ Description:jmh使用第三个例子
 * @ Author     :xiaojiang
 * @ Date       :Created in 2019-06-19
 * @ Version    :0.0.1
 */
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.SingleShotTime)
@Warmup(iterations = 5)
@Measurement(iterations = 1)
@State(Scope.Benchmark)
@Fork(1)
public class JmhDemoThree {
    public static void main(String[] args) throws Exception{
        Options options = new OptionsBuilder()
                .include(JmhDemoThree.class.getName())
                .build();
        new Runner(options).run();
    }
    /**
     * 序列化次数
     */
    @Param({"100", "10000", "1000000"})
    private int number;
    private Userinfo userinfo;
    private String fastjson_jsonStr;
    private String gson_jsonStr;
    private String jackson_jsonStr;
    /**
     *  fastjson bean2Json
     */
    @Benchmark
    public void fastjson_bean2Json(){
        for (int i=0;i<number;i++){
            JsonUtil.fastjson_bean2Json(userinfo);
        }
    }
    /**
     *  gson bean2Json
     */
    @Benchmark
    public void gson_bean2Json(){
        for (int i=0;i<number;i++){
            JsonUtil.gson_bean2Json(userinfo);
        }
    }
    /**
     *  jackson bean2Json
     */
    @Benchmark
    public void jackson_bean2Json(){
        for (int i=0;i<number;i++){
            JsonUtil.jackson_bean2Json(userinfo);
        }
    }
    /**
     *  fastjson json2Bean
     */
    @Benchmark
    public void fastjson_json2Bean(){
        for (int i=0;i<number;i++){
            JsonUtil.fastjson_json2Bean(fastjson_jsonStr,Userinfo.class);
        }
    }
    /**
     *  gson json2Bean
     */
    @Benchmark
    public void gson_json2Bean(){
        for (int i=0;i<number;i++){
            JsonUtil.gson_json2Bean(gson_jsonStr,Userinfo.class);
        }
    }
    /**
     *  jackson json2Bean
     */
    @Benchmark
    public void jackson_json2Bean(){
        for (int i=0;i<number;i++){
            JsonUtil.jackson_json2Bean(jackson_jsonStr,Userinfo.class);
        }
    }
    /**
     * 初始化参数
     */
    @Setup
    public void init(){
         userinfo = new Userinfo();
        userinfo.setUsername("张三");
        userinfo.setGender("男");
        userinfo.setAge(18);
        userinfo.setBirthday(new Date());
        userinfo.setCreateTime(System.currentTimeMillis());
        List<String> list = new ArrayList<>();
        list.add("北京三里屯儿那条街那条巷那一号");
        list.add("上海三里屯儿那条街那条巷那一号");
        list.add("深圳三里屯儿那条街那条巷那一号");
        userinfo.setAddress(list);

        fastjson_jsonStr = JsonUtil.fastjson_bean2Json(userinfo);
        gson_jsonStr = JsonUtil.gson_bean2Json(userinfo);
        jackson_jsonStr = JsonUtil.jackson_bean2Json(userinfo);
    }
}

(其它相关代码后附)

测试结果:

//---省略测试过程结果----------
Benchmark                        (number)  Mode  Cnt     Score   Error  Units
JmhDemoThree.fastjson_bean2Json       100    ss          1.586          ms/op
JmhDemoThree.fastjson_bean2Json     10000    ss          3.683          ms/op
JmhDemoThree.fastjson_bean2Json   1000000    ss        500.924          ms/op
JmhDemoThree.fastjson_json2Bean       100    ss          0.978          ms/op
JmhDemoThree.fastjson_json2Bean     10000    ss          5.493          ms/op
JmhDemoThree.fastjson_json2Bean   1000000    ss        362.337          ms/op
JmhDemoThree.gson_bean2Json           100    ss          2.106          ms/op
JmhDemoThree.gson_bean2Json         10000    ss         28.693          ms/op
JmhDemoThree.gson_bean2Json       1000000    ss       1890.999          ms/op
JmhDemoThree.gson_json2Bean           100    ss          7.175          ms/op
JmhDemoThree.gson_json2Bean         10000    ss        110.298          ms/op
JmhDemoThree.gson_json2Bean       1000000    ss       7310.555          ms/op
JmhDemoThree.jackson_bean2Json        100    ss          2.111          ms/op
JmhDemoThree.jackson_bean2Json      10000    ss          8.859          ms/op
JmhDemoThree.jackson_bean2Json    1000000    ss        376.587          ms/op
JmhDemoThree.jackson_json2Bean        100    ss          1.992          ms/op
JmhDemoThree.jackson_json2Bean      10000    ss         10.723          ms/op
JmhDemoThree.jackson_json2Bean    1000000    ss        714.569          ms/op

从测试结果可以看出,不论是bean2Json还是json2Bean,fastjson的性能比gson、jackson都要好一些,当然,jackson性能也很不错(不愧是spring默认的序列化和反序列化工具),尤其是当序列化与反序列化次数较多时,fastjson优势尤其明显。当然,由于笔者用于测试的实体bean数据结构还是较为简单,在一些较为复杂的数据结构场景下,其各自的性能表现可能有所不一样。(笔者用的测试Mode是Mode.SingleShotTime,只测试一次,且由于机器等原因,所以误差可能相对较大。有兴趣的读者,可以测试一下不同测试Mode下,更复杂的数据结构场景下,各序列化/反序列化工具的性能表现)

四、小结

1、JMH官方并没有提供比较详细的使用文档(这也是笔者整理本文的重要原因),但是其提供了许多详细、较容易理解的例子,有问题的可以参考,地址为:http://hg.openjdk.java.net/code-tools/jmh/file/99d7b73cf1e3/jmh-samples/src/main/java/org/openjdk/jmh/samples
2、JMH中的参数配置,许多参数可以直接在main方法的options中设置,也可以通过在类上直接添加注解配置。
3、注意:跑测试的时候要直接用run的方式跑,不要用debug的方式跑,否则会出错。
4、JMH适用于方法级别的基准性能测试,并不适用于跨系统、跨服务之间的全链路测试。
5、使用JMH基准测试,虽然精度可以达到微妙级,但是测试结果依然是会存在一定误差的;由于测试机器、场景、jvm配置等不同而引起测试误差是完全可能的,只是这个误差能否在可接受的范围内。
6、最终测试结果是fork参数与每次测试迭代参数的合集,如fork值为3,iterations值为5,那最终测试次数就是 3 * 5 = 15次。

参考文章:
http://hg.openjdk.java.net/code-tools/jmh/file/99d7b73cf1e3/jmh-samples/src/main/java/org/openjdk/jmh/samples
http://java-performance.info/jmh/
https://www.cnblogs.com/tranquillity/p/9488572.html
https://www.xncoding.com/2018/01/07/java/jmh.html
https://blog.csdn.net/lxbjkben/article/details/79410740
http://blog.dyngr.com/blog/2016/10/29/introduction-of-jmh/
http://irfen.me/java-jmh-simple-microbenchmark/
https://www.cnblogs.com/bestzhang/p/10082119.html

附:

json工具pom依赖:

<!--fastJson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.58</version>
</dependency>
<!--gson-->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>
<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.9</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.9</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.9</version>
</dependency>

Userinfo.java:

package com.xiaojiang;

import java.util.Date;
import java.util.List;
/**
 * @ Description:用户信息
 * @ Author     :xiaojiang
 * @ Date       :Created in 2019-06-19
 * @ Version    :0.0.1
 */
public class Userinfo {
    private String username;    //用户名
    private String gender;      //用户性别
    private Integer age;    //用户年龄
    private Date birthday;      //用户生日
    private List<String> address;   //  用户地址
    private Long createTime;    //用户创建时间

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public List<String> getAddress() {
        return address;
    }

    public void setAddress(List<String> address) {
        this.address = address;
    }

    public Long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Userinfo{" +
                "username='" + username + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                ", address=" + address +
                ", createTime=" + createTime +
                '}';
    }
}

JsonUtil.java:

package com.xiaojiang;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;

import java.io.IOException;

/**
 * @ Description:json工具类
 * @ Author     :xiaojiang
 * @ Date       :Created in 2019-06-19
 * @ Version    :0.0.1
 */
public class JsonUtil {

    private static com.google.gson.Gson gson = new com.google.gson.GsonBuilder().create();
    private static com.fasterxml.jackson.databind.ObjectMapper jacksonMapper = new com.fasterxml.jackson.databind.ObjectMapper();

    public static String fastjson_bean2Json(Object object){
        return com.alibaba.fastjson.JSON.toJSONString(object);
    }

    public static <T> T fastjson_json2Bean(String jsonStr, Class<T> objectClass) {
        return JSON.parseObject(jsonStr, objectClass);
    }

    public static String gson_bean2Json(Object object){
        return gson.toJson(object);
    }

    public static <T> T gson_json2Bean(String jsonStr, Class<T> objectClass){
        return gson.fromJson(jsonStr,objectClass);
    }

    public static String jackson_bean2Json(Object object) {
        try {
            return jacksonMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T jackson_json2Bean(String jsonStr, Class<T> objectClass){
        try {
            return jacksonMapper.readValue(jsonStr,objectClass);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

到此这篇关于详解Java使用JMH进行基准性能测试的文章就介绍到这了,更多相关Java 基准性能测试内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈Java基准性能测试之JMH

    目录 一.JMH vs JMeter 二.JMH基本用法 2.1.创建JMH项目 2.2.编写基准测试代码 2.3.JMH打包.运行 2.4.JMH与Springboot 三.JMH注解 3.1.JMH Benchmark Modes 3.2.Benchmark Time Units 3.3.Benchmark State 3.4.State Object @Setup @TearDown 3.5.Fork 3.6.Thread 3.7.Warmup 3.8.Measurement 四.输出测试

  • 详解Java使用JMH进行基准性能测试

    目录 一.前言 二.JMH概述 1.什么是JMH 2.JMH适用的典型场景 3.JMH基本概念 三.JMH的使用 1.快速跑起来 2.JMH常用注解详细介绍 四.小结 附: 一.前言 在日常开发工作当中,开发人员可能有这些困惑:自己写的这个方法性能到底怎么样?在原接口实现方法中添加了新的业务逻辑,对整个接口的性能影响有多少?有多种实现方式(或开源类库),到底哪一种性能更好?- 当遇到类似困惑或者说问题的时候,怎么办呢?当然是测试验证,实践出真知!本文讲述的就是一个方法级别的性能测试工具--JMH

  • 详解JAVA 时间处理相关类

    时间处理相关类: 1.java.util.Date:时间类 2.java.text.DateFormat:时间格式化类(抽象类),实现类:java.text.SimpleDateFormat 3.java.util.Calendar:日历类(抽象类),实现类:java.util.GergorianCalendar 1.java.util.Date 时间就是一个数轴,在计算机中,1970年1月1日00:00:00定位基准时间,也就是数轴的原点,每个度量单位是毫秒(1000毫秒=1秒) java中我

  • 详解java 中的CAS与ABA

    1. 独占锁: 属于悲观锁,有共享资源,需要加锁时,会以独占锁的方式导致其它需要获取锁才能执行的线程挂起,等待持有锁的钱程释放锁.传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.Java中synchronized和ReentrantLock等独占锁就是悲观锁的思想. 1.1 乐观锁的操作 多线程并发修改一个值时的实现: public class SimulatedCAS { //加volatile的目的是利用其happens-before原则

  • 详解Java编译优化之循环展开和粗化锁

    循环展开和粗化锁 我们先来回顾一下什么是循环展开. 循环展开就是说,像下面的循环遍历的例子: for (int i = 0; i < 1000; i++) { x += 0x51; } 因为每次循环都需要做跳转操作,所以为了提升效率,上面的代码其实可以被优化为下面的: for (int i = 0; i < 250; i++) { x += 0x144; //0x51 * 4 } 注意上面我们使用的是16进制数字,至于为什么要使用16进制呢?这是为了方便我们在后面的assembly代码中快速找

  • 详解Java中@Override的作用

    详解Java中@Override的作用 @Override是伪代码,表示重写(当然不写也可以),不过写上有如下好处: 1.可以当注释用,方便阅读: 2.编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错.例如,你如果没写@Override,而你下面的方法名又写错了,这时你的编译器是可以编译通过的,因为编译器以为这个方法是你的子类中自己增加的方法. 举例:在重写父类的onCreate时,在方法前面加上@Override 系统可以帮你检查方法的正确性. @Overr

  • 详解Java中多线程异常捕获Runnable的实现

    详解Java中多线程异常捕获Runnable的实现 1.背景: Java 多线程异常不向主线程抛,自己处理,外部捕获不了异常.所以要实现主线程对子线程异常的捕获. 2.工具: 实现Runnable接口的LayerInitTask类,ThreadException类,线程安全的Vector 3.思路: 向LayerInitTask中传入Vector,记录异常情况,外部遍历,判断,抛出异常. 4.代码: package step5.exception; import java.util.Vector

  • 详解Java编写并运行spark应用程序的方法

    我们首先提出这样一个简单的需求: 现在要分析某网站的访问日志信息,统计来自不同IP的用户访问的次数,从而通过Geo信息来获得来访用户所在国家地区分布状况.这里我拿我网站的日志记录行示例,如下所示: 121.205.198.92 - - [21/Feb/2014:00:00:07 +0800] "GET /archives/417.html HTTP/1.1" 200 11465 "http://shiyanjun.cn/archives/417.html/" &qu

  • 详解java 中Spring jsonp 跨域请求的实例

    详解java 中Spring jsonp 跨域请求的实例 jsonp介绍 JSONP(JSON with Padding)是JSON的一种"使用模式",可用于解决主流浏览器的跨域数据访问的问题.由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外.利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSO

  • 详解Java线程池和Executor原理的分析

    详解Java线程池和Executor原理的分析 线程池作用与基本知识 在开始之前,我们先来讨论下"线程池"这个概念."线程池",顾名思义就是一个线程缓存.它是一个或者多个线程的集合,用户可以把需要执行的任务简单地扔给线程池,而不用过多的纠结与执行的细节.那么线程池有哪些作用?或者说与直接用Thread相比,有什么优势?我简单总结了以下几点: 减小线程创建和销毁带来的消耗 对于Java Thread的实现,我在前面的一篇blog中进行了分析.Java Thread与内

  • 详解java调用存储过程并封装成map

    详解java调用存储过程并封装成map 本文代码中注释写的比较清楚不在单独说明,希望能帮助到大家, 实例代码: public List<Map<String , Object>> doCallProcedure(String procedureString,String[] parameters) throws PersistentDataOperationException { if (!isReady ()) { throw new PersistentDataOperatio

随机推荐