0Pricing
Java Academy · Lesson

Why JMH

Avoid naive benchmark mistakes.

Why JMH is a free Java Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why JMH

The Java Microbenchmark Harness (JMH) is the standard tool for measuring the performance of small pieces of Java code. Hand-rolled timing loops almost always produce wrong numbers because the JVM is a sophisticated optimizing runtime.

JMH exists to neutralize those pitfalls.

The Naive Benchmark

A typical first attempt wraps a loop in System.nanoTime(). It looks reasonable but is deeply flawed for microbenchmarks.

public class Main {
    static int compute(int n) { return n * n + 7; }
    public static void main(String[] args) {
        long start = System.nanoTime();
        int sink = 0;
        for (int i = 0; i < 1_000_000; i++) sink = compute(i);
        long elapsed = System.nanoTime() - start;
        System.out.println("ns: " + elapsed + " sink=" + sink);
    }
}

Problem 1: JIT Warmup

The JVM starts in interpreted mode and only compiles hot methods to native code after thousands of invocations. A naive benchmark measures the slow interpreted phase mixed with the fast compiled phase, giving meaningless averages.

JMH solves this with a dedicated warmup phase that is discarded.

Problem 2: Dead-Code Elimination

If a computed result is never used, the JIT may delete the computation entirely. Your benchmark then measures an empty loop.

JMH provides return-value consumption and the Blackhole to defeat this.

Problem 3: Constant Folding

If inputs are compile-time constants, the JIT computes the answer once and reuses it. Below, the compiler could replace the whole loop with a single constant.

JMH uses @State objects so inputs are opaque to the optimizer.

public class Main {
    public static void main(String[] args) {
        // 2 * 21 is constant; the JIT folds it to 42
        int result = 2 * 21;
        System.out.println(result);
    }
}

Problem 4: Loop Optimizations

The JIT unrolls loops, hoists invariant code, and vectorizes operations. A hand-written loop measures these optimizations rather than the operation you intended to test.

JMH replaces your loop with carefully controlled iteration counts it manages itself.

Problem 5: On-Stack Replacement

A long-running loop in main can be compiled mid-execution (on-stack replacement), producing performance characteristics that differ from a normally compiled method. This skews results unpredictably.

What JMH Provides

  • Separate, discarded warmup iterations.
  • Multiple measurement iterations with statistics.
  • Forking into fresh JVMs to avoid profile pollution.
  • Blackhole and result consumption against dead-code elimination.
  • @State objects to defeat constant folding.

How You Run It

JMH is a separate dependency (org.openjdk.jmh) and is normally launched by an annotation processor plus a Maven/Gradle build that produces an executable JAR. You do not run benchmarks from a plain main like ordinary code.

Statistics Matter

JMH reports not just an average but the error / confidence interval across iterations and forks. A result of 42.0 +/- 1.3 ns/op tells you both the central value and how much it varied — essential for trusting a measurement.

When to Reach for JMH

Use JMH when comparing two implementations of a hot path, validating an optimization, or measuring nanosecond-to-microsecond operations. For coarse, second-scale work (I/O, network), wall-clock timing is usually adequate.

Quick Check

Test your understanding of why JMH is needed.

Recap

You learned why microbenchmarking needs a harness:

  • The JVM warms up: interpreted code becomes compiled only after many calls.
  • Dead-code elimination and constant folding can delete or precompute your work.
  • Loop optimizations and on-stack replacement distort hand-written loops.
  • JMH adds warmup, measurement iterations, forking, Blackhole, and statistics.
  • Reach for it when measuring nanosecond-scale hot paths.

Frequently asked questions

Is the “Why JMH” lesson free?

Yes — the full text of “Why JMH” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.

What will I learn in “Why JMH”?

Avoid naive benchmark mistakes. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Java Academy?

No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Why JMH” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Java Academy lesson?

Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Why JMH
  2. Writing a Benchmark
  3. Warmup and Iterations
  4. Avoiding Dead-Code Elimination
← Back to Java Academy