Зачем нужен JMH
Избегайте наивных ошибок при измерениях
«Зачем нужен JMH» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Зачем нужен JMH
Каркас микротестирования Java (JMH) — стандартный инструмент для измерения производительности небольших фрагментов кода Java. Самописные циклы измерения времени почти всегда дают неверные значения, поскольку JVM — это сложная оптимизирующая среда выполнения.
JMH создан для устранения этих проблем.
Наивный микротест
Обычно первая попытка заключается в том, чтобы обернуть цикл в System.nanoTime(). Это выглядит разумно, но для микротестов такой подход содержит серьёзные недостатки.
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);
}
}Проблема 1: прогрев JIT
JVM запускается в интерпретируемом режиме и компилирует горячие методы в машинный код только после тысяч вызовов. Наивный микротест измеряет медленную интерпретируемую фазу вместе с быстрой скомпилированной фазой, поэтому средние значения теряют смысл.
JMH решает эту проблему с помощью отдельной фазы прогрева, результаты которой отбрасываются.
Проблема 2: устранение мёртвого кода
Если вычисленный результат нигде не используется, JIT может полностью удалить вычисление. Тогда Ваш микротест измеряет пустой цикл.
JMH предоставляет использование возвращаемых значений и Blackhole, чтобы предотвратить это.
Проблема 3: свёртка констант
Если входные данные являются константами времени компиляции, JIT вычисляет ответ один раз и повторно использует его. В приведённом ниже примере компилятор может заменить весь цикл одной константой.
JMH использует объекты @State, чтобы входные данные оставались непрозрачными для оптимизатора.
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);
}
}Проблема 4: оптимизация циклов
JIT разворачивает циклы, выносит неизменяемый код и векторизует операции. Самописный цикл измеряет эти оптимизации, а не ту операцию, которую Вы хотели проверить.
JMH заменяет Ваш цикл тщательно контролируемым количеством итераций, которым управляет самостоятельно.
Проблема 5: замена на стеке
Долго выполняющийся цикл в main может быть скомпилирован во время выполнения (с помощью замены на стеке), что даёт характеристики производительности, отличающиеся от характеристик обычного скомпилированного метода. Это непредсказуемо искажает результаты.
Что предоставляет JMH
- Отдельные итерации прогрева, результаты которых отбрасываются.
- Несколько итераций измерения со статистикой.
- Запуск в новых JVM, чтобы избежать загрязнения профилей.
- Поглотитель и использование результатов для защиты от устранения мёртвого кода.
- Объекты @State для предотвращения свёртки констант.
Как это запускать
JMH является отдельной зависимостью (org.openjdk.jmh) и обычно запускается с помощью обработчика аннотаций и сборки Maven/Gradle, создающей исполняемый JAR. Микротесты не запускают из обычного main, как обычный код.
Статистика важна
JMH сообщает не только среднее значение, но и погрешность / доверительный интервал по всем итерациям и отдельным запускам. Результат 42.0 +/- 1.3 ns/op показывает и центральное значение, и степень его изменчивости — это необходимо для доверия к измерению.
Когда использовать JMH
Используйте JMH при сравнении двух реализаций горячего участка кода, проверке оптимизации или измерении операций длительностью от наносекунд до микросекунд. Для продолжительных операций, занимающих секунды (ввод-вывод, сеть), обычно достаточно измерения реального времени.
Быстрая проверка
Проверьте, насколько хорошо Вы понимаете, зачем нужен JMH.
Итоги
Вы узнали, зачем микротестированию нужен специальный каркас:
- JVM прогревается: интерпретируемый код становится скомпилированным только после множества вызовов.
- Устранение мёртвого кода и свёртка констант могут удалить или заранее вычислить Вашу работу.
- Оптимизации циклов и замена на стеке искажают самописные циклы.
- JMH добавляет прогрев, итерации измерения, запуск в отдельных JVM, поглотитель и статистику.
- Используйте его при измерении горячих участков кода, выполняющихся за наносекунды.
Часто задаваемые вопросы
Урок «Зачем нужен JMH» бесплатный?
Да — полный текст урока «Зачем нужен JMH» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Зачем нужен JMH»?
Избегайте наивных ошибок при измерениях Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Зачем нужен JMH»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Зачем нужен JMH
- Написание измерения производительности
- Прогрев и итерации
- Предотвращение удаления неиспользуемого кода