Working with JMH (Microbenchmarking)
1. Setting Up JMH Project
Step Detail
Generate mvn archetype:generate -Dgroup=org.openjdk.jmh -DartifactId=jmh-archetype
Dependencies jmh-core, jmh-generator-annprocess
Build Shade JMH runner JAR
Run java -jar target/benchmarks.jar
2. Creating Benchmark Methods (@Benchmark)
Example: Simple benchmark
@ Benchmark
public int hashCode (MyState s) {
return s.value. hashCode ();
}
Rule Detail
Return result Or use Blackhole
No I/O Unless intentionally measuring it
3. Using Benchmark Modes
Mode Reports
Throughput ops/time
AverageTime time/op
SampleTime Sampled time distribution
SingleShotTime One iteration (cold)
All All of the above
4. Configuring Warmup (@Warmup)
Param Default
iterations 5
time 10 s
batchSize 1
Purpose Reach steady state (JIT, caches)
5. Configuring Measurement (@Measurement)
Param Use
iterations 5–10 typical
time 10 s typical
forks JVM forks (default 5)
threads Concurrent worker count
6. Using State Objects (@State)
Example: State
@ State (Scope.Benchmark)
public static class MyState {
String value;
@ Setup public void init () { value = "hello" ; }
}
Aspect Detail
Lifecycle Linked to Scope
Injection Pass as @Benchmark method param
7. Understanding Scope
Scope Sharing
Benchmark One instance for all threads
Group Shared in @Group methods
Thread Per-thread instance
8. Using Setup and TearDown (@Setup, @TearDown)
Level Runs
Trial Once per benchmark run
Iteration Each iteration
Invocation Every @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 ());
}
Issue Fix
Unused result Return it OR Blackhole.consume()
Constant folding Read input from @State
10. Running Benchmarks
Command Use
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
Field Meaning
Score Mean throughput / time
Error 99.9% confidence half-width
Ratio Compare to baseline
Visualize jmh-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.
Pitfall Effect
Constant folding JIT precomputes result
Inlining hides cost Use Blackhole
No warmup Measures interpreter
Single fork Class init bias