Выявление узких мест
Сначала измеряйте, затем оптимизируйте
«Выявление узких мест» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Измеряйте, не гадайте
Первое правило работы с производительностью: сначала измеряйте, а потом оптимизируйте.
Интуитивные представления о том, где Java-программа проводит время, обычно ошибочны. JIT-компилятор, сборщик мусора и кэширование делают предположения ненадёжными. Выполните профилирование, найдите настоящее узкое место и исправьте его.
Что такое узкое место
Узкое место — это часть системы, ограничивающая общую пропускную способность или задержку.
Оптимизация чего-либо другого не даст заметной пользы. Закон Амдала формулирует это точно: если 90% времени приходится на один метод, ускорение остальных 10% никогда не даст улучшения более чем на 11%.
Задержка и пропускная способность
Определите, что именно Вы оптимизируете:
- Задержка — сколько времени занимает один запрос.
- Пропускная способность — сколько запросов обрабатывается в секунду.
Между ними есть компромисс. Пакетная обработка повышает пропускную способность, но может увеличить задержку отдельного запроса. Перед настройкой определите целевой показатель.
Измерение реального времени
Самое простое измерение — замерить реальное время вокруг блока кода с помощью System.nanoTime().
Это полезно для быстрой проверки здравого смысла, но результат включает прогрев JIT, паузы сборщика мусора и шум планирования OS, поэтому к отдельным значениям следует относиться с осторожностью.
public class Main {
public static void main(String[] args) {
long start = System.nanoTime();
long sum = 0;
for (int i = 0; i < 10_000_000; i++) sum += i;
long elapsed = System.nanoTime() - start;
System.out.println("Sum: " + sum);
System.out.println("Elapsed ms: " + (elapsed / 1_000_000.0));
}
}Остерегайтесь прогрева JIT
Java начинает с интерпретации байткода, а затем JIT компилирует часто выполняемые методы в машинный код.
Поэтому первые запуски метода намного медленнее последующих. Наивный цикл измерения в основном измеряет прогрев. Настоящие тесты производительности сначала выполняют прогрев, а затем измеряют установившийся режим — именно это за Вас делает JMH.
Ограничение CPU и ограничение IO
Определите тип узкого места:
- Ограничение CPU — потоки заняты вычислениями, а ядра полностью загружены.
- Ограничение IO — потоки ожидают диск, сеть или базу данных.
Профилировщики различают это как время «на CPU» и «заблокированное или ожидающее». Исправления полностью различаются: более быстрые алгоритмы либо больший параллелизм и меньшее число обращений.
Выборка и инструментирование
Есть две стратегии профилирования:
- Выборка — периодически получать трассировки стека. Низкие накладные расходы, статистический результат.
- Инструментирование — добавлять счётчики в каждый метод. Точный результат, но большие накладные расходы, которые могут исказить измерения.
В рабочей среде предпочитайте выборку с низкими накладными расходами, например с помощью Java Flight Recorder.
Память как узкое место
Часто настоящая стоимость связана с выделением памяти, а не с вычислениями. Избыточное создание объектов приводит к частому запуску сборки мусора, отнимает ресурсы CPU и добавляет паузы.
Следите за скоростью выделения памяти и временем сборки мусора. Уменьшение числа выделений в часто выполняемом цикле нередко даёт больший эффект, чем мелкая настройка арифметических операций.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// Allocation-heavy: a new String each iteration
List<String> garbage = new ArrayList<>();
for (int i = 0; i < 5; i++) {
garbage.add("item-" + i);
}
System.out.println("Allocated " + garbage.size() + " strings");
System.out.println("In a hot loop, this churn drives GC pressure");
}
}Найдите вершину стека
Профилировщик с выборкой создаёт список методов, упорядоченных по частоте появления в стеке CPU, — это собственное время.
Метод на вершине списка — Ваш кандидат. Но убедитесь, что он находится на критическом пути: часто выполняемый метод фонового компонента журналирования может не влиять на задержку, которую видит пользователь.
Создайте исходную точку
Прежде чем что-либо менять, зафиксируйте измерение исходного состояния при реалистичной нагрузке.
После каждого изменения измеряйте снова и сравнивайте результаты. Без исходной точки невозможно доказать, что оптимизация помогла, а многие «оптимизации» только ухудшают ситуацию. Меняйте по одному параметру за раз.
Профилирование при реалистичной нагрузке
Узкое место, обнаруженное на бездействующем ноутбуке, может оказаться не тем, которое будет вредить в рабочей среде.
- Используйте репрезентативные размеры данных и уровень параллелизма.
- Воспроизводите рабочую нагрузку, которая действительно важна для пользователей.
Синтетические микро-тесты могут указать на метод, не имеющий значения в большом масштабе. Профилируйте там, где действительно возникает проблема.
Быстрая проверка
Почему одиночные наивные измерения времени выполнения метода Java с помощью System.nanoTime() часто вводят в заблуждение?
Итоги
Поиск узких мест дисциплинированным способом:
- Измеряйте перед оптимизацией; интуиция обманывает.
- Выберите цель: задержка или пропускная способность.
- Определите, ограничена ли работа CPU или IO; следите за сборкой мусора и выделением памяти.
- Отдавайте предпочтение профилировщикам с низкими накладными расходами, использующим выборку.
- Учитывайте прогрев компилятора во время выполнения; установите базовый показатель и изменяйте только один параметр за раз.
Часто задаваемые вопросы
Урок «Выявление узких мест» бесплатный?
Да — полный текст урока «Выявление узких мест» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Выявление узких мест»?
Сначала измеряйте, затем оптимизируйте Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Выявление узких мест»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Выявление узких мест
- Java Flight Recorder
- Анализ с помощью JDK Mission Control
- Распространённые флаги настройки JVM