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 methodMode: Measurement type (average execution time, throughput, latency percentile, etc.)Cnt: Number of valid measurement iterations completedScore: Calculated performance metric value for the test caseError: 99.9% confidence interval error marginUnits: 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.