Язык и JVM — Примеры

Пример 1 — Бенчмарк, который измеряет интерпретатор

Команда замеряет изменение одним прогоном на несколько тысяч итераций, а потом спорит о результате, который между прогонами меняется вдвое. Перепишите измерение так, чтобы оно выходило на устойчивый режим.

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; }
}

Решение

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; }
}

Код начинает исполняться интерпретатором, а горячие пути компилируются, как только среда выполнения их увидит. Поэтому ранние партии измеряют интерпретатор, а поздние — программу, и печать каждой партии показывает, где числа перестают улучшаться. Обычная ошибка — усреднять прогрев в результат или сравнивать два числа, которые оба ещё падают. Числа в примере условны; измеряйте свою нагрузку.

Пример 2 — Зависимость, которой не хватает только во время работы

Проект компилируется на сборочной машине, однако classpath артефакта собирается отдельно; один путь использует класс из библиотеки, нужной только для компиляции, и падает внутри запроса спустя долгое время после чистого запуска. Определите, чего не хватает и почему отказ пришёл именно тогда.

import com.example.payments.PaymentClient;   // только для компиляции, нет во время работы

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

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

Подставьте координаты своей библиотеки вместо условной выше.

Решение

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

# классы загружаются при первом использовании, поэтому отсутствующий или
# несовместимый класс отказывает здесь, а не при запуске

Ошибка называет класс, и это весь диагноз: она указывает на объявление, которое должно было принести класс в рабочий classpath. Зависимость времени компиляции служит обработчиком аннотаций или заглушкой API; всё остальное должно присутствовать при запуске программы. Ошибка в том, чтобы считать, будто всё, что компилируется, есть и во время работы. Дерево при этом оставляют непроверенным до первого отказа запроса.