You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JMH中如何实现JUnit@BeforeAll/@BeforeClass的等效功能?

JMH实现全局仅执行一次Setup的方案

解决方案1:使用静态代码块

静态代码块在类加载阶段仅执行一次,完全符合你想要的「类似JUnit @BeforeAll」的效果,无需依赖JMH的Setup级别:

package de.shd.kps.backend.customer.benchmark;

import java.util.concurrent.TimeUnit;

import org.junit.runner.RunWith;
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.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.springframework.test.context.junit4.SpringRunner;

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@RunWith(SpringRunner.class)
public class ExampleTest extends AbstractBenchmarkHarnessForTests
{
   private static int setupCalls = 0;

   // 静态代码块:类加载时仅执行一次
   static {
      setupCalls++;
      System.err.println("Setup-Calls:");
      System.err.println(setupCalls);
   }

   @Benchmark
   public void benchmark()
   {
   }

   @Benchmark
   public void benchmark2()
   {
   }

   @Benchmark
   public void benchmark3()
   {
   }
}

解决方案2:使用Scope.Global的独立State类

如果你需要依赖JMH的生命周期管理(比如初始化逻辑需要在JMH启动后执行),可以将初始化逻辑放到一个Scope.Global的State类中,这个State会在整个基准测试生命周期中仅初始化一次:

package de.shd.kps.backend.customer.benchmark;

import java.util.concurrent.TimeUnit;

import org.junit.runner.RunWith;
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.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.springframework.test.context.junit4.SpringRunner;

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@RunWith(SpringRunner.class)
public class ExampleTest extends AbstractBenchmarkHarnessForTests
{
   // 全局唯一的State类
   @State(Scope.Global)
   public static class GlobalInitState {
       int setupCalls = 0;

       @Setup(Level.Trial)
       public void setup() {
           setupCalls++;
           System.err.println("Setup-Calls:");
           System.err.println(setupCalls);
       }
   }

   // Benchmark方法注入全局State
   @Benchmark
   public void benchmark(GlobalInitState globalState)
   {
       // 可使用globalState中的初始化资源
   }

   @Benchmark
   public void benchmark2(GlobalInitState globalState)
   {
   }

   @Benchmark
   public void benchmark3(GlobalInitState globalState)
   {
   }
}

为什么原来的配置会触发3次Setup

你遇到的问题核心原因是:JMH会将同一个类中的每个@Benchmark方法视为独立的测试任务,每个任务都会启动自己的Trial流程。即使使用Scope.Benchmark的State,每个Benchmark任务的Trial都会触发一次@Setup(Level.Trial),因此3个Benchmark方法会导致3次Setup调用。

而Scope.Global的State是全局共享的,所有Benchmark方法都会复用同一个实例,因此只会执行一次Trial级别的Setup;静态代码块则完全脱离JMH的生命周期,类加载时仅执行一次。

内容的提问来源于stack exchange,提问作者Hag2bard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 02:20:20