The language and the JVM — Examples

Example 1 — A benchmark that measures the interpreter

A team measures a change with a single timed run of a few thousand iterations, then argues about a result that moves by a factor of two between runs. Rewrite the measurement so it reports steady state.

public class Bench {
    public static void main(String[] args) {
        long start = System.nanoTime();
        long total = 0;
        for (int i = 0; i < 10_000; i++) total += work(i);
        System.out.println((System.nanoTime() - start) / 1_000_000 + " ms");
    }

    static long work(int i) { return i % 7; }
}

Solution

public class Bench {
    static final int BATCHES = 20;
    static final int PER_BATCH = 100_000;

    public static void main(String[] args) {
        long total = 0;
        for (int b = 0; b < BATCHES; b++) {
            long start = System.nanoTime();
            for (int i = 0; i < PER_BATCH; i++) total += work(i);
            long ms = (System.nanoTime() - start) / 1_000_000;
            if (b >= 3) System.out.println("batch " + b + ": " + ms + " ms");
        }
        System.out.println(total);
    }

    static long work(int i) { return i % 7; }
}

Code starts interpreted and hot paths are compiled once the runtime has observed them, so the early batches measure the interpreter and the later ones measure the program; printing each batch shows where the numbers stop improving. The usual mistake is to average the warm-up into the result, or to compare two numbers that are both still falling. Figure values are illustrative; measure your own load.

Example 2 — A dependency missing only at runtime

The project compiles on the build machine, but the artifact’s classpath is assembled separately. One path uses a class from a library needed only to compile, and it fails inside a request long after a clean startup. Identify what is missing and why it arrived then.

import com.example.payments.PaymentClient;   // compile-only, absent at run time

public class OrderService {
    private final PaymentClient client = new PaymentClient();

    Receipt place(Order order) {
        return client.charge(order.total());
    }
}

Substitute your own library’s coordinates for the placeholder above.

Solution

java.lang.NoClassDefFoundError: com/example/payments/PaymentClient
    at OrderService.<init>(OrderService.java:4)

# classes load on first use, so an absent or incompatible one fails here,
# not at startup

The error names the class, and that is the whole diagnostic: it points at the declaration that should have brought the class onto the runtime classpath. A compile-time dependency is an annotation processor or an API stub; everything else must be present when the program runs. The mistake is to assume that anything which compiles is there at runtime, and to leave the tree uninspected until a request fails.