Working with JMH (Microbenchmarking)

1. Setting Up JMH Project

StepDetail
Generatemvn archetype:generate -Dgroup=org.openjdk.jmh -DartifactId=jmh-archetype
Dependenciesjmh-core, jmh-generator-annprocess
BuildShade JMH runner JAR
Runjava -jar target/benchmarks.jar

2. Creating Benchmark Methods (@Benchmark)

Example: Simple benchmark

@Benchmark
public int hashCode(MyState s) {
    return s.value.hashCode();
}
RuleDetail
Return resultOr use Blackhole
No I/OUnless intentionally measuring it

3. Using Benchmark Modes

ModeReports
Throughputops/time
AverageTimetime/op
SampleTimeSampled time distribution
SingleShotTimeOne iteration (cold)
AllAll of the above

4. Configuring Warmup (@Warmup)

ParamDefault
iterations5
time10 s
batchSize1
PurposeReach steady state (JIT, caches)

5. Configuring Measurement (@Measurement)

ParamUse
iterations5–10 typical
time10 s typical
forksJVM forks (default 5)
threadsConcurrent worker count

6. Using State Objects (@State)

Example: State

@State(Scope.Benchmark)
public static class MyState {
    String value;
    @Setup public void init() { value = "hello"; }
}
AspectDetail
LifecycleLinked to Scope
InjectionPass as @Benchmark method param

7. Understanding Scope

ScopeSharing
BenchmarkOne instance for all threads
GroupShared in @Group methods
ThreadPer-thread instance

8. Using Setup and TearDown (@Setup, @TearDown)

LevelRuns
TrialOnce per benchmark run
IterationEach iteration
InvocationEvery @Benchmark call (use sparingly — bias risk)

9. Avoiding Dead Code Elimination (Blackhole)

Example: Blackhole

@Benchmark
public void multi(Blackhole bh) {
    bh.consume(work1());
    bh.consume(work2());
}
IssueFix
Unused resultReturn it OR Blackhole.consume()
Constant foldingRead input from @State

10. Running Benchmarks

CommandUse
java -jar benchmarks.jarRun all
java -jar benchmarks.jar PatternFilter by name regex
-prof gcAllocation profiler
-prof async:libPath=...;output=flamegraphasync-profiler
-rf json -rff out.jsonJSON output

11. Analyzing Results

FieldMeaning
ScoreMean throughput / time
Error99.9% confidence half-width
RatioCompare to baseline
Visualizejmh-visualizer (web UI)

12. Understanding JMH Pitfalls

Warning: Microbenchmarks are easy to write but hard to interpret. Always: (1) cross-check with macro-benchmark, (2) verify dead-code isn't elided, (3) use multiple forks, (4) account for warmup, (5) compare on the same JVM/hardware.
PitfallEffect
Constant foldingJIT precomputes result
Inlining hides costUse Blackhole
No warmupMeasures interpreter
Single forkClass init bias