如何使用JMH对包含库导入耗时的Java代码进行基准测试?
JMH基准测试:关于“库导入开销”的真相
先搞清楚一个关键事实:Java里的import语句完全没有运行时开销。它只是编译阶段的语法糖——让你写代码时不用反复写全限定类名(比如直接写List而非java.util.List),编译后的字节码里所有类引用都是全限定名,和导入语句半毛钱关系都没有。所以不存在“把库导入和代码一起基准测试”的说法,因为导入本身根本不影响运行时性能。
如果你实际想测的是首次加载某个类时的JVM类加载开销(比如第一次用IntStream或Collections时JVM加载这些类的耗时),这和导入语句无关,但可以用JMH来测试,具体做法如下:
因为JMH默认会执行预热迭代,预热时类就会被加载,正式测试就测不到加载开销了,所以要做以下调整:
- 用
@Fork让每个测试在独立JVM进程运行,确保每次测试都是全新的类加载环境 - 关闭预热,确保测试时类是首次被加载
- 用
SingleShotTime模式测单次执行的开销(类加载只发生一次)
示例代码:
package org.example; import org.openjdk.jmh.annotations.*; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; @Fork(value = 1) // 每个测试fork新JVM,避免类加载状态复用 public class ClassLoadBenchmark { // 测试首次加载类+执行逻辑的总开销 @Benchmark @BenchmarkMode(Mode.SingleShotTime) @Warmup(iterations = 0) // 关闭预热,确保类首次加载 public void bench_FirstRunWithClassLoad() { List<Integer> pages = IntStream.range(0, 256).boxed().collect(Collectors.toList()); Collections.shuffle(pages); } // 对比基准:仅测逻辑本身的开销(类已提前加载完成) @Benchmark @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5) // 预热让类加载完成 @Measurement(iterations = 10) public void bench_LogicOnly() { List<Integer> pages = IntStream.range(0, 256).boxed().collect(Collectors.toList()); Collections.shuffle(pages); } public static void main(String[] args) throws Exception { org.openjdk.jmh.Main.main(args); } }
运行后对比两个基准的结果,差值就是首次类加载带来的额外开销。
内容的提问来源于stack exchange,提问作者Ymi
相关产品推荐
相关产品推荐

