性能调优之JMH必知必会1:什么是JMH

  • JMH必知必会系列文章(持续更新)
  • 一、前言
  • 二、什么是JMH
    • 1、JMH简介
    • 2、JMH入门
    • 3、使用JMH进行微基准测试

JMH必知必会系列文章(持续更新)

  • 性能调优之JMH必知必会2:JMH的基本用法
  • 性能调优之JMH必知必会3:编写正确的微基准测试用例
  • 性能调优之JMH必知必会4:JMH的高级用法
  • 性能调优之JMH必知必会5:JMH的Profiler

一、前言

  性能调优一直是工作中很重要的必会技能,如何知晓自己写代码的优劣呢?当然是看代码运行时间,时间越短,说明代码越优。
 
  不太严谨的一种测试代码性能的方法是在要测量性能的代码开头位置写“ long startTime = System.currentTimeMillis(); ”,结束位置写“ long endTime = System.currentTimeMillis(); ”,然后计算“ endTime - startTime ”的值,得出运行时长。或者使用 guavaStopwatch 类,测试的代码开头位置写“ Stopwatch stopwatch = Stopwatch.createStarted(); ” ,结束位置写“ long duration = stopwatch.stop().elapsed(TimeUnit.MILLISECONDS); ”,运行时长就是long类型的duration。在代码的运行过程中,JVM可能会对其进行运行时的优化,比如循环展开、运行时编译等,这样会导致某组未经优化的性能数据参与统计计算。
 
  那么有没有一种严谨的比较准确地测量出代码性能的工具呢?JMH作为主角出场了。【单位换算:1秒(s)=1000000微秒(us)=1000000000纳秒(ns)
 
  官方JMH源码(包含样例,在jmh-samples包里)下载地址:https://github.com/openjdk/jmh/tags。
 
  官方JMH样例在线浏览地址:http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/。
 
  本文内容参考书籍《Java高并发编程详解:深入理解并发核心库》,作者为 汪文君 ,读者有需要可以去购买正版书籍。
 
  本文由 @大白有点菜 原创,请勿盗用,转载请说明出处!如果觉得文章还不错,请点点赞,加关注,谢谢!

二、什么是JMH

1、JMH简介

  JMH(Java Microbenchmark Harness)是一个 Java 工具,用于构建、运行和分析用 Java 和其他针对 JVM 的语言编写的 纳米/微米/毫/宏观 基准测试,而且是由Java虚拟机团队开发的。简单说,就是用来测量代码运行性能。
 
  JMH官网:https://openjdk.org/projects/code-tools/jmh/
  JMH源码下载:https://github.com/openjdk/jmh

2、JMH入门

(1)先演示一个不太严谨测量代码性能的例子,使用常见的ArrayList和LinkedList中add方法。在main函数里面,循环10次,每次对List执行1000000次的add操作,然后通过最大值、最小值、平均值的方式对其进行对比(当然,这里并不考虑不同容器的内存开销,事实上,ArrayList和LinkedList在使用的过程中,它们的内存开销肯定是不一样的)。
 
【pom.xml文件添加 guava 包】

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.0.1-jre</version><scope>compile</scope>
</dependency>

【核心代码】

package cn.zhuangyt.javabase.jmh;import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;/*** JMH必知必会* @author 大白有点菜*/
public class JmhApp {/*** 测试数据*/private final static String DATA = "大白有点菜";/*** add操作次数*/private final static int ADD_COUNT = 1000000;/*** 迭代(循环)次数*/private final static int MAX_ITERATIONS = 10;/*** 测试方法* @param list*/private static void test(List<String> list){for (int i = 0; i < ADD_COUNT; i++){list.add(DATA);}}/*** ArrayList性能测试* @param iterations 迭代(循环)次数*/private static void arrayListPerfTest(int iterations){for (int i = 0; i < iterations; i++){final List<String> list = new ArrayList<>();final Stopwatch stopwatch = Stopwatch.createStarted();test(list);System.out.println(new StringBuilder().append("ArrayList第 ").append(i+1).append(" 次循环add操作").append(ADD_COUNT).append("次耗费时间为:").append(stopwatch.stop().elapsed(TimeUnit.MILLISECONDS)).append("毫秒"));}}/*** LinkedList性能测试* @param iterations 迭代(循环)次数*/private static void linkedListPerfTest(int iterations){for (int i = 0; i < iterations; i++){final List<String> list = new LinkedList<>();final Stopwatch stopwatch = Stopwatch.createStarted();test(list);System.out.println(new StringBuilder().append("LinkedList第 ").append(i+1).append(" 次循环add操作").append(ADD_COUNT).append("次耗费时间为:").append(stopwatch.stop().elapsed(TimeUnit.MILLISECONDS)).append("毫秒"));}}public static void main(String[] args) {arrayListPerfTest(MAX_ITERATIONS);System.out.println(Strings.repeat("#", 48));linkedListPerfTest(MAX_ITERATIONS);}
}

【运行结果】

ArrayList第 1 次循环add操作1000000次耗费时间为:13毫秒
ArrayList第 2 次循环add操作1000000次耗费时间为:12毫秒
ArrayList第 3 次循环add操作1000000次耗费时间为:9毫秒
ArrayList第 4 次循环add操作1000000次耗费时间为:8毫秒
ArrayList第 5 次循环add操作1000000次耗费时间为:7毫秒
ArrayList第 6 次循环add操作1000000次耗费时间为:5毫秒
ArrayList第 7 次循环add操作1000000次耗费时间为:6毫秒
ArrayList第 8 次循环add操作1000000次耗费时间为:7毫秒
ArrayList第 9 次循环add操作1000000次耗费时间为:10毫秒
ArrayList第 10 次循环add操作1000000次耗费时间为:7毫秒
##################################################
LinkedList第 1 次循环add操作1000000次耗费时间为:8毫秒
LinkedList第 2 次循环add操作1000000次耗费时间为:42毫秒
LinkedList第 3 次循环add操作1000000次耗费时间为:4毫秒
LinkedList第 4 次循环add操作1000000次耗费时间为:7毫秒
LinkedList第 5 次循环add操作1000000次耗费时间为:38毫秒
LinkedList第 6 次循环add操作1000000次耗费时间为:4毫秒
LinkedList第 7 次循环add操作1000000次耗费时间为:7毫秒
LinkedList第 8 次循环add操作1000000次耗费时间为:6毫秒
LinkedList第 9 次循环add操作1000000次耗费时间为:9毫秒
LinkedList第 10 次循环add操作1000000次耗费时间为:9毫秒

(2)从上面的例子运行结果,得出ArrayList和LinkedList的add方法性能对比折线图。如下:
 

循环次数/对比的类 ArrayList LinkedList
1 13 8
2 12 42
3 9 4
4 8 7
5 7 38
6 5 4
7 6 7
8 7 6
9 10 9
10 7 9
平均值 8.4 13.4
  • ArrayList:最大耗时13毫秒,最小耗时5毫秒,平均耗时8.4毫秒。
  • LinkedList:最大耗时42毫秒,最小耗时4毫秒,平均耗时13.4毫秒。

(3)从性能对比折线图看出,似乎ArrayList的add方法性能要好于LinkedList的add方法。事实上,ArrayList的随机读写性能确实要好于LinkedList(尤其是在ArrayList不进行内部扩容数组复制的情况下),LinkedList由于链表的设计,其delete操作的性能会好于ArrayList。不过这种测试存在诸多问题,具体列举如下。

  • 使用Stopwatch进行时间计算,其实是在Stopwatch内部记录了方法执行的开始纳秒数,这种操作本身会导致一些CPU时间的浪费。
  • 在代码的运行过程中,JVM可能会对其进行运行时的优化,比如循环展开、运行时编译等,这样会导致某组未经优化的性能数据参与统计计算。
  • arrayListPerfTest 方法和 linkedListPerfTest 的运行环境并不公平,比如,在第一个测试方法执行的过程中或者执行结束后,其所在的JVM进程或许已经进行了profiler的优化,还有第一个测试方法所开辟的内存有可能也未被释放。

3、使用JMH进行微基准测试

(1)添加JMH依赖包,在Maven仓库中搜索依赖包jmh-corejmh-generator-annprocess ,版本为 1.36 。需要注释 jmh-generator-annprocess 包中的“<scope>test</scope>”,不然项目运行会报错。

<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>1.36</version>
</dependency><!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-generator-annprocess</artifactId><version>1.36</version>
<!--            <scope>test</scope>-->
</dependency>

(2)即使照搬书中的代码,笔者在运行测试时,居然报错了,难道书中代码是错的?
 
【核心代码,代码运行会报错】

package cn.zhuangyt.javabase.jmh;import org.openjdk.jmh.annotations.*;
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 java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;/*** JMH测试1* @author 大白有点菜*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class JmhTestApp1 {private final static String DATA = "大白有点菜";private List<String> arrayList;private List<String> linkedList;/*** 初始化 ArrayList 和 LinkedList*/@Setup(Level.Iteration)public void setUp(){this.arrayList = new ArrayList<>();this.linkedList = new LinkedList<>();}/*** ArrayList的add方法* @return*/@Benchmarkpublic List<String> arrayListAdd(){this.arrayList.add(DATA);return arrayList;}/*** LinkedList的add方法* @return*/@Benchmarkpublic List<String> linkedListAdd(){this.linkedList.add(DATA);return this.linkedList;}public static void main(String[] args) throws RunnerException {final Options opts = newOptionsBuilder().include(JmhTestApp1.class.getSimpleName()).forks(1).measurementIterations(10).warmupIterations(10).build();new Runner(opts).run();}
}

(3)第一次运行并不顺利,因为报了个 OutOfMemoryError: Java heap space(Java堆空间内存溢出)错误,先贴报错代码,再来分析并解决问题。报错日志如下:

java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:3181)at java.util.ArrayList.grow(ArrayList.java:267)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)at java.util.ArrayList.add(ArrayList.java:464)at cn.zhuangyt.javabase.jmh.JmhTestApp1.arrayListAdd(JmhTestApp1.java:45)at cn.zhuangyt.javabase.jmh.jmh_generated.JmhTestApp1_arrayListAdd_jmhTest.arrayListAdd_avgt_jmhStub(JmhTestApp1_arrayListAdd_jmhTest.java:188)at cn.zhuangyt.javabase.jmh.jmh_generated.JmhTestApp1_arrayListAdd_jmhTest.arrayListAdd_AverageTime(JmhTestApp1_arrayListAdd_jmhTest.java:152)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:475)at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:458)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)

  报错位置指向第45行,即 this.arrayList.add(DATA) 方法。如图所示:
 
 
(4)经过反复验证,笔者终于发现问题所在,现在就来分析报错原因和分享解决方法。
 
 1)要关注这5行报错日志,倒序来查找原因。

at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:267)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)
at java.util.ArrayList.add(ArrayList.java:464)

 2)既然是 ArrayList.add 方法报错,那就看看源码,看里面是怎么实现add功能的。

/*** Appends the specified element to the end of this list.** @param e element to be appended to this list* @return <tt>true</tt> (as specified by {@link Collection#add})*/
public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;
}

  add方法里面包含一个 ensureCapacityInternal 方法和 elementData 数组。ensureCapacityInternal 是ArrayList的内部扩容方法,有些面试会问到ArrayList的特点是什么,自动扩容是其中的一个特点。ArrayList在add数据时,会先判断容量够不够,不够就扩容。elementData 数组是ArrayList底层的数据结构,数据就是存储在 elementData 数组里。变量 size 是elementData数组的索引。elementData 前面的 transient 关键字代表对应的成员变量不可被序列化
 

 3)进入到 ensureCapacityInternal 方法里,看到调用的是 ensureExplicitCapacity 方法。ensureExplicitCapacity 方法的入参是int类型,调用的是 calculateCapacity 方法来计算容量并返回int类型的值。

private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

 4)进入到 calculateCapacity 方法里看看代码是怎样实现容量计算的。静态常量 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 代表默认数组容量为空,静态常量 DEFAULT_CAPACITY 代表默认数组初始容量为10,如图所示。先判断数组 elementData 是否为空,为空的情况下,比较默认容量 DEFAULT_CAPACITY 和 最小容量 minCapacity 的大小,取最大值的那一个。

private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}


 
 5)再进入到 ensureExplicitCapacity 方法里。如果最小容量 minCapacity 大于 elementData 数组的大小,那就调用 grow 方法进行扩容

private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}

 6)grow 方法里,定义局部变量 oldCapacity 为旧容量,局部变量 newCapacity 为新容量,newCapacity 初始值为 oldCapacity加上oldCapacity的一半(oldCapacity >> 1 代表oldCapacity除以2)。如果 newCapacity 大于 静态变量 MAX_ARRAY_SIZE (常量值为 Integer.MAX_VALUE - 8)的值,就调用 hugeCapacity 方法扩大容量

private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);
}

 7)在 hugeCapacity 方法里面,先判断 minCapacity 的值是否小于0,小于0就抛出 OutOfMemoryError 异常!问题原来出在这里,minCapacity 的值为负数导致抛出超出内存错误异常,即OOM。

private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}

 8)为什么 minCapacity 的值会为负数呢?因为 minCapacity 变量是int类型,最大值为 2147483647 ,如果超出,就变成负数!如图所示。那导致 minCapacity 超出最大值的原因是什么呢?笔者认为,那可能是JMH执行太快而且执行时间长所致,ArrayList一直在快速扩容,JVM也同时在进行垃圾回收,当扩容的速度超过垃圾回收的速度,内存也就爆满而溢出了。
 

 9)笔者查阅了资料,发现注解 @Measurement(iterations = xxx, time = xxx, timeUnit = TimeUnit.xxx)iterationstimetimeUnit 三个参数是同时配置的。timeUnit 缺省,代表 time 的单位为 秒(s)。
 
  @Measurement的 iterations 变量等同于 measurementIterations(int count) 方法,@Measurement的 time 变量等同于 measurementTime(TimeValue value) 方法,@Measurement的 timeUnit 变量等同于 measurementTime 的形式参数 TimeValue value。注解 @Warmup(iterations = xxx, time = xxx, timeUnit = TimeUnit.xxx) 也是类似。
 
  其实呢,前面OOM出现的原因,就是因为没有在 measurementTime(TimeValue value) 方法指定参数类型和 warmupTime(TimeValue value) 方法指定参数类型,即 TimeValue 的类型,两者传入的默认参数值而导致报错!

public static void main(String[] args) throws RunnerException {final Options opts = new OptionsBuilder().include(JmhTestApp1.class.getSimpleName()).forks(1).measurementIterations(10).warmupIterations(10).build();new Runner(opts).run();
}

 10)OptionsBuilder 类实现 ChainedOptionsBuilder 接口measurementTimewarmupTimeChainedOptionsBuilder 接口的抽象方法。两个方法对应的参数默认值都在 Defaults 类里。measurementTime 方法的参数对应MEASUREMENT_TIME,默认值为TimeValue.seconds(10)warmupTime 方法的参数对应WARMUP_TIME,默认值为 TimeValue.seconds(10)

/*** How long each measurement iteration should take?* @param value time* @return builder* @see org.openjdk.jmh.annotations.Measurement* @see org.openjdk.jmh.runner.Defaults#MEASUREMENT_TIME*/
ChainedOptionsBuilder measurementTime(TimeValue value);
/*** Warmup mode to use* @param mode to use* @return builder* @see org.openjdk.jmh.runner.Defaults#WARMUP_MODE*/
ChainedOptionsBuilder warmupMode(WarmupMode mode);


 

 

 
 11)笔者使用的是台式电脑,CPU为6代i7,4核8线程,性能不差,JDK版本为 JDK 1.8.0_281。在测试代码中,类上面使用@OutputTimeUnit(TimeUnit.MICROSECONDS)注解,代表输出时间单位是微秒级别的,1秒(s)=1000000微秒(us)=1000000000纳秒(ns)。如果使用默认的 TimeValue.seconds(10) ,持续10秒时间进行10次微秒级别的 measurementIterations(度量迭代)和10次 warmupIterations(预热迭代),你们说,这么长的时间,ArrayList扩容的速度能不快和容量能不超过最大值吗?JVM进行垃圾回收根本来不及啊!
 

 
 12)综上问题分析所述,既然找到问题的关键,要解决问题就好办了,控制measurementIterations(度量迭代)和warmupIterations(预热迭代)的持续时间不就行了吗。main函数修改如下,设置 measurementTime 和 warmupTime 的持续时间为 1000000微秒,即1s。

public static void main(String[] args) throws RunnerException {final Options opts = new OptionsBuilder().include(JmhTestApp1.class.getSimpleName()).forks(1).measurementIterations(10).measurementTime(TimeValue.microseconds(1000000L)).warmupIterations(10).warmupTime(TimeValue.microseconds(1000000L)).build();new Runner(opts).run();
}

(5)修改后的核心代码和运行结果如下。
 
【核心代码,修改后的代码运行正常,若报错,将运行时间设置小些】

package cn.zhuangyt.javabase.jmh;import org.openjdk.jmh.annotations.*;
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 org.openjdk.jmh.runner.options.TimeValue;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;/*** JMH测试1* @author 大白有点菜*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class JmhTestApp1 {/*** 测试数据*/private final static String DATA = "大白有点菜";private List<String> arrayList = new ArrayList<>();private List<String> linkedList = new LinkedList<>();/*** 初始化 ArrayList 和 LinkedList*/@Setup(Level.Iteration)public void setUp(){this.arrayList = new ArrayList<>();this.linkedList = new LinkedList<>();}/*** ArrayList的add方法* @return*/@Benchmarkpublic List<String> arrayListAdd() {this.arrayList.add(DATA);return arrayList;}/*** LinkedList的add方法* @return*/@Benchmarkpublic List<String> linkedListAdd() {this.linkedList.add(DATA);return this.linkedList;}public static void main(String[] args) throws RunnerException {final Options opts = new OptionsBuilder().include(JmhTestApp1.class.getSimpleName()).forks(1).measurementIterations(10).measurementTime(TimeValue.microseconds(1000000L)).warmupIterations(10).warmupTime(TimeValue.microseconds(1000000L)).build();new Runner(opts).run();}
}

【运行结果】

# JMH version: 1.36
# VM version: JDK 1.8.0_281, Java HotSpot(TM) 64-Bit Server VM, 25.281-b09
# VM invoker: D:\Develop\JDK\jdk1.8.0_281\jre\bin\java.exe
# VM options: -javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=5391:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 10 iterations, 1000000 us each
# Measurement: 10 iterations, 1000000 us each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: cn.zhuangyt.javabase.jmh.JmhTestApp1.arrayListAdd# Run progress: 0.00% complete, ETA 00:00:40
# Fork: 1 of 1
# Warmup Iteration   1: 0.016 us/op
# Warmup Iteration   2: 0.016 us/op
# Warmup Iteration   3: 0.011 us/op
# Warmup Iteration   4: 0.016 us/op
# Warmup Iteration   5: 0.012 us/op
# Warmup Iteration   6: 0.010 us/op
# Warmup Iteration   7: 0.011 us/op
# Warmup Iteration   8: 0.022 us/op
# Warmup Iteration   9: 0.012 us/op
# Warmup Iteration  10: 0.014 us/op
Iteration   1: 0.012 us/op
Iteration   2: 0.010 us/op
Iteration   3: 0.010 us/op
Iteration   4: 0.010 us/op
Iteration   5: 0.010 us/op
Iteration   6: 0.010 us/op
Iteration   7: 0.011 us/op
Iteration   8: 0.011 us/op
Iteration   9: 0.026 us/op
Iteration  10: 0.024 us/opResult "cn.zhuangyt.javabase.jmh.JmhTestApp1.arrayListAdd":0.013 ±(99.9%) 0.009 us/op [Average](min, avg, max) = (0.010, 0.013, 0.026), stdev = 0.006CI (99.9%): [0.004, 0.023] (assumes normal distribution)# JMH version: 1.36
# VM version: JDK 1.8.0_281, Java HotSpot(TM) 64-Bit Server VM, 25.281-b09
# VM invoker: D:\Develop\JDK\jdk1.8.0_281\jre\bin\java.exe
# VM options: -javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=5391:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 10 iterations, 1000000 us each
# Measurement: 10 iterations, 1000000 us each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: cn.zhuangyt.javabase.jmh.JmhTestApp1.linkedListAdd# Run progress: 50.00% complete, ETA 00:00:23
# Fork: 1 of 1
# Warmup Iteration   1: 0.311 us/op
# Warmup Iteration   2: 0.106 us/op
# Warmup Iteration   3: 0.150 us/op
# Warmup Iteration   4: 0.065 us/op
# Warmup Iteration   5: 0.150 us/op
# Warmup Iteration   6: 0.064 us/op
# Warmup Iteration   7: 0.155 us/op
# Warmup Iteration   8: 0.158 us/op
# Warmup Iteration   9: 0.150 us/op
# Warmup Iteration  10: 0.054 us/op
Iteration   1: 0.140 us/op
Iteration   2: 0.025 us/op
Iteration   3: 7.116 us/op
Iteration   4: 0.146 us/op
Iteration   5: 0.054 us/op
Iteration   6: 0.141 us/op
Iteration   7: 0.054 us/op
Iteration   8: 0.142 us/op
Iteration   9: 0.054 us/op
Iteration  10: 0.142 us/opResult "cn.zhuangyt.javabase.jmh.JmhTestApp1.linkedListAdd":0.802 ±(99.9%) 3.355 us/op [Average](min, avg, max) = (0.025, 0.802, 7.116), stdev = 2.219CI (99.9%): [≈ 0, 4.157] (assumes normal distribution)# Run complete. Total time: 00:01:13REMEMBER: 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
JmhTestApp1.arrayListAdd   avgt   10  0.013 ± 0.009  us/op
JmhTestApp1.linkedListAdd  avgt   10  0.802 ± 3.355  us/op

  最后两行反映了arrayListAdd方法的调用平均响应时间为0.013微秒,误差在0.009微秒,而linkedListAdd方法的调用平均响应时间为0.802微秒,误差在3.355微秒,很明显,前者的性能要高于后者。

性能调优之JMH必知必会1:什么是JMH相关推荐

  1. 关于ES性能调优几件必须知道的事

    2019独角兽企业重金招聘Python工程师标准>>> (零)ElasticSearch架构概述 ElasticSearch是现在技术前沿的大数据引擎,常见的组合有ES+Logsta ...

  2. 关于ElasticSearch性能调优几件必须知道的事

    (零)ElasticSearch架构概述 ElasticSearch是现在技术前沿的大数据引擎,常见的组合有ES+Logstash+Kibana作为一套成熟的日志系统,其中Logstash是ETL工具 ...

  3. 性能调优之JMH必知必会2:JMH的基本用法

    性能调优之JMH必知必会2:JMH的基本用法 JMH必知必会系列文章(持续更新) 一.前言 二.JMH的基本用法 1.添加JMH依赖包 2.@Benchmark 2.@Warmup和@Measurem ...

  4. MySQL性能调优必知:Performance Schema引擎的配置与使用

    当你在MySQL高并发情况下的进行性能调优时,需要知道调整后的影响.例如查询是否变快了?锁是否会减慢运行速度?内存使用情况如何?磁盘IO等待时间变了吗? . Performance Schema就有一 ...

  5. 如何开启jvm日志_直通BAT必考题系列:JVM性能调优的6大步骤,及关键调优参数详解...

    JVM系列 直通BAT必考题系列:7种JVM垃圾收集器特点,优劣势.及使用场景 直通BAT必考题系列:JVM的4种垃圾回收算法.垃圾回收机制与总结 直通BAT必考题系列:深入详解JVM内存模型与JVM ...

  6. 系统性能调优必知必会(脑图更新。。)

    系统性能调优必知必会 CPU缓存 CPU多级缓存,单核心会有自己的一级二级缓存,所有核心会共享三级缓存 一级缓存包括数据缓存 .指令缓存. 数据的读取是以缓存行进行读取的,缓存行一般为64字节. 缓存 ...

  7. 「入门运维必看」一篇让小白彻底搞懂性能调优!

    前言: 什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么人来进行性能调优?(who) 怎么样进行性能调优?( ...

  8. Android 性能优化必知必会(2020-5-16)

    做了这么久性能相关的工作,也接触了不少模块,说实话要做好性能这一块,真心不容易.为什么这么说? 是因为需要接触的知识实在是太多了, Android 是一个整体,牵一发而动全身,不是说只懂一个模块就可以 ...

  9. Java架构师必知必会,带走不谢

    可以说,Java是现阶段中国互联网公司中,覆盖度最广的研发语言,掌握了Java技术体系,不管在成熟的大公司,快速发展的公司,还是创业阶段的公司,都能有立足之地. 成为Java架构师,需要掌握哪些技能呢 ...

最新文章

  1. openshift scc解析
  2. UIView自定义控件-Swfit
  3. Windows XP硬盘安装Ubuntu 11.10双系统全程图解
  4. 二级c语言努力学可以过吗,考过计算机二级C语言一些感想和建议
  5. python 获取当天和前几天时间数据(亲测)
  6. 《Stellaris》游戏分析报告
  7. 网络知识:说说我们常听说的网络攻击是怎么回事?
  8. ElasticSearch7.2只能用localhost访问但不能用IP地址访问---ElasticSearch工作笔记027
  9. DataSet.Tables[].Rows[][]的用法
  10. Linux复习-进程及其管理
  11. 从端到云——工业物联网项目全栈快速开发实践
  12. Bath Body 純白花漾系列
  13. 科学大家谈 | 全球著名蛋白质工程学家——张阳
  14. 当代音乐杂志当代音乐杂志社当代音乐编辑部2022年第12期目录
  15. Android常用加密解密实现方式
  16. python pandas AttributeError: ‘DataFrame‘ object has no attribute ‘ix‘
  17. UML总结之九种模型图
  18. ELISPOT酶联免疫斑点技术说明书
  19. Docker安装最新版ES8.4.1
  20. H5 IOS webview设置背景透明

热门文章

  1. java对word文档的操作
  2. 红米2 手机root
  3. 如何设计空白页面,体验更好!
  4. 利用Joypy绘制嵴线图的案例
  5. 咸阳无房证明网上办理指南
  6. python在线查题_知到题目在线答案查询
  7. 公司邮箱怎么注册?公司企业邮箱怎么登录?
  8. 【Netty官方文档翻译】引用计数对象(reference counted objects)
  9. 微信上线支付分对标芝麻信用分,教你如何开通!
  10. 华为路由器学习指南_BGP_路由反射器与联盟