Master Java Performance Testing with JMH: A Complete Guide and Demo

This article explains why measuring Java code performance is essential, introduces the JMH microbenchmark framework, details its key annotations and configuration options, and provides step‑by‑step instructions and code examples for building, writing, and running benchmarks to obtain reliable results.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Master Java Performance Testing with JMH: A Complete Guide and Demo

Introduction

Accurate performance measurement is crucial for Java development because the JVM mixes interpreted and JIT‑compiled execution, making it hard to determine stable test results without proper warm‑up.

If you cannot measure it, you cannot improve it.

JMH (Java Microbenchmark Harness) is an official OpenJDK tool designed for micro‑benchmarking at the method level with microsecond precision.

Key Points for Java Benchmarking

Warm up the code before measuring.

Avoid dead code elimination in benchmark methods.

Use concurrent testing when appropriate.

Present results clearly.

When to Use JMH

Quantitatively analyze the effect of optimizing a hotspot function.

Determine execution time and its relation to input variables.

Compare multiple implementations of a function.

Demo Project Setup

JMH works with Java 9+; the example uses Java 8.

Build a Maven project with the JMH archetype:

$ mvn archetype:generate \
    -DinteractiveMode=false \
    -DarchetypeGroupId=org.openjdk.jmh \
    -DarchetypeArtifactId=jmh-java-benchmark-archetype \
    -DgroupId=org.sample \
    -DartifactId=test \
    -Dversion=1.0

Alternatively, add the following dependencies to an existing Maven project:

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>${jmh.version}</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>${jmh.version}</version>
    <scope>provided</scope>
</dependency>

Writing a Benchmark

The example compares LinkedList iteration via index versus foreach:

/**
 * @author Richard_yyf
 * @version 1.0 2019/8/27
 */
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@Threads(Threads.MAX)
public class LinkedListIterationBenchMark {
    private static final int SIZE = 10000;
    private List<String> list = new LinkedList<>();

    @Setup
    public void setUp() {
        for (int i = 0; i < SIZE; i++) {
            list.add(String.valueOf(i));
        }
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    public void forIndexIterate() {
        for (int i = 0; i < list.size(); i++) {
            list.get(i);
            System.out.print("");
        }
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    public void forEachIterate() {
        for (String s : list) {
            System.out.print("");
        }
    }
}

Running the Benchmark

Execute via generated JAR:

$ mvn clean install
$ java -jar target/benchmarks.jar

Or run directly from a main method:

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
        .include(LinkedListIterationBenchMark.class.getSimpleName())
        .forks(1)
        .warmupIterations(2)
        .measurementIterations(2)
        .output("E:/Benchmark.log")
        .build();
    new Runner(opt).run();
}

Result Example

Benchmark                                 Mode  Cnt   Score   Error  Units
LinkedListIterationBenchMark.forEachIterate   thrpt    2 1192.380          ops/s
LinkedListIterationBenchMark.forIndexIterate  thrpt    2  206.866          ops/s

Annotation Overview

Key JMH annotations:

@BenchmarkMode : Defines benchmark mode (Throughput, AverageTime, SampleTime, SingleShotTime, All).

@Warmup : Number of warm‑up iterations.

@Measurement : Number of measurement iterations, time, and time unit.

@Threads : Number of threads used.

@Fork : Number of JVM forks.

@OutputTimeUnit : Unit for reporting results.

@Benchmark : Marks a method as a benchmark.

@Param : Supplies multiple values for a field.

@Setup and @TearDown : Prepare and clean up resources.

@State : Declares a class as shared state with a defined scope (Thread, Group, Benchmark).

Conclusion

JMH enables reliable micro‑benchmarking of Java code, allowing developers to evaluate optimizations, compare implementations, and understand performance characteristics. For more examples, refer to the official JMH samples repository.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaPerformance TestingBenchmarkmicrobenchmarkJMH
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.