How to Implement and Interpret Java Performance Benchmarks Using JMH

Performance tuning for Java applications relies on accurate, reproducible performance measurements to identify bottlenecks and validate optimization effects. JMH (Java Microbenchmark Harness), an official OpenJDK subproject, is purpose-built for creating, executing, and parsing microbenchmark results for Java and JVM-based languages, eliminating common pitfalls like JIT optimization interference that can skew manual test results.

Dependency Configuration

For Maven projects, add the following JMH dependencies to your pom.xml file. The example uses the latest stable 1.37 release:

<dependencies>
    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-core</artifactId>
        <version>1.37</version>
    </dependency>
    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-generator-annprocess</artifactId>
        <version>1.37</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Benchmark Test Case Implementation

Define test classes with JMH annotations to control test execution rules, state lifecycle, and output formats. The following example compares performance of string concatenation using the + operator versus StringBuilder:

import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;

@Fork(1)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@State(Scope.Benchmark)
public class StringConcatBenchmark {
    private static final int REPEAT_COUNT = 1000;

    @Benchmark
    public String concatWithPlusOperator() {
        String result = "";
        for (int i = 0; i < REPEAT_COUNT; i++) {
            result += "test" + i;
        }
        return result;
    }

    @Benchmark
    public String concatWithStringBuilder() {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < REPEAT_COUNT; i++) {
            builder.append("test").append(i);
        }
        return builder.toString();
    }
}

Benchmark Execution

You can run tests directly via JMH-compatible IDE plugins, or create a dedicated launcher class to pass custom execution parameters:

import org.openjdk.jmh.Main;

public class JmhExecutionLauncher {
    public static void main(String[] args) throws Exception {
        // Filter test targets, set output format and export path
        Main.main(new String[]{"StringConcatBenchmark", "-rf", "csv", "-rff", "benchmark-results.csv"});
    }
}

Result Interpretation

Standard JMH output includes the following core fields:

  • Benchmark: Full identifier of the executed test method
  • Mode: Measurement type (average execution time, throughput, latency percentile, etc.)
  • Cnt: Number of valid measurement iterations completed
  • Score: Calculated performance metric value for the test case
  • Error: 99.9% confidence interval error margin
  • Units: Unit of the reported performance score

Lower average time scores indicate faster code execution, while higher throughput scores indicate better processing capacity. To ensure result reliability, run tests in an idle, isolated environment, avoid shared hardware resources during testing, and configure sufficient warmup iterations to let JIT optimizations reach steady state before formal measurement.

Tags: java JMH Performance Testing Benchmarking Code Optimization

Posted on Tue, 12 May 2026 18:56:40 +0000 by Darkwoods