0Pricing
Java Academy · Lesson

Writing a Benchmark

@Benchmark and modes.

Writing a Benchmark is a free Java Academy lesson on CoddyKit — lesson 2 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.

Writing a Benchmark

A JMH benchmark is just a method annotated with @Benchmark inside a regular class. JMH's annotation processor generates the harness around it. This lesson covers the annotations that define what and how you measure.

The @Benchmark Annotation

Mark any public method with @Benchmark and JMH will repeatedly invoke it, timing each call. The method's return value should be returned (not discarded) so JMH can consume it.

A Minimal Benchmark Class

Here is the shape of a benchmark. The import is org.openjdk.jmh.annotations.*. Because JMH is not on this runner's classpath, this is illustrative only.

import org.openjdk.jmh.annotations.Benchmark;

public class StringBench {
    @Benchmark
    public String concat() {
        return "foo" + System.nanoTime();
    }
}

Benchmark Modes

@BenchmarkMode chooses what is reported:

  • Throughput — operations per unit time (higher is better).
  • AverageTime — time per operation (lower is better).
  • SampleTime — distribution of times (percentiles).
  • SingleShotTime — one invocation, good for cold-start.
import org.openjdk.jmh.annotations.*;

public class Bench {
    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    public int work() {
        return 2 + 2;
    }
}

Choosing Output Units

@OutputTimeUnit sets the time unit for the report so numbers are readable. For a fast operation, nanoseconds make sense; for a slow one, milliseconds.

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

public class Bench {
    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public int work() {
        return 42;
    }
}

Returning Results

Always return the result of the work. JMH feeds returned values into a Blackhole automatically, preventing dead-code elimination. A void benchmark that computes and discards is a classic mistake.

Annotation Defaults

Annotations can sit on the method or on the whole class. Class-level annotations apply to every @Benchmark in that class, which keeps configuration DRY when you have several related benchmarks.

Forking

@Fork(n) runs the benchmark in n fresh JVM processes. Forking isolates each run from JIT profiles built up by previous benchmarks, improving reproducibility. The default is one fork.

import org.openjdk.jmh.annotations.*;

@Fork(2)
public class Bench {
    @Benchmark
    public long now() {
        return System.nanoTime();
    }
}

Launching the Runner

You start benchmarks via a main that configures an Options object and a Runner, or via the generated executable JAR. The include(...) regex selects which benchmarks to run.

import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.*;

public class Launcher {
    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
            .include("Bench")
            .build();
        new Runner(opt).run();
    }
}

Reading the Output

JMH prints a table with the benchmark name, mode, sample count, the score, the score error, and units, for example: Bench.work avgt 25 3.142 +/- 0.011 ns/op. The +/- is the confidence interval.

Keep Benchmarks Focused

Each benchmark should measure one thing. Mixing setup, I/O, and the operation under test muddies the result. Move preparation into @Setup methods (covered with state) so only the hot path is timed.

Quick Check

Test your understanding of writing benchmarks.

Recap

You learned how to write a JMH benchmark:

  • Annotate a method with @Benchmark and return the result.
  • @BenchmarkMode picks Throughput, AverageTime, SampleTime, or SingleShotTime.
  • @OutputTimeUnit sets readable units; @Fork isolates runs in fresh JVMs.
  • Launch with a Runner + Options or the generated JAR.
  • Keep each benchmark focused on a single hot path.

Frequently asked questions

Is the “Writing a Benchmark” lesson free?

Yes — the full text of “Writing a Benchmark” 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 “Writing a Benchmark”?

@Benchmark and modes. 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 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Writing a Benchmark” 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