Оптимизация производительности Java: восемь антипаттернов, замедляющих ваш код

Улучшение производительности за счет исправления антипаттернов
Джонатан Фогел создал приложение для обработки заказов на Java, которое изначально имело время выполнения 1198 мс, обрабатывало 85 000 заказов в секунду, использовало чуть более 1 ГБ кучи и имело 19 пауз сборки мусора. После исправления восьми антипаттернов без изменений архитектуры или обновлений JDK производительность улучшилась до 239 мс времени выполнения, 419 000 заказов в секунду, 139 МБ кучи и 4 пауз сборки мусора. Это представляет собой 5-кратную пропускную способность, на 87% меньшее использование кучи и на 79% меньше пауз сборки мусора.
Восемь антипаттернов производительности Java, которые нужно исправить
- Конкатенация строк в циклах - O(n²) копирование из-за неизменяемости
- Итерация потоков O(n²) внутри циклов - потоковая обработка всего списка для каждого элемента
- String.format() в горячих путях - самый медленный построитель строк, парсинг формата при каждом вызове
- Автоупаковка в горячих путях - миллионы одноразовых объектов-обёрток
- Исключения для управления потоком выполнения - fillInStackTrace() проходит по всему стеку вызовов
- Слишком широкие блокировки - одна блокировка становится узким местом
- Пересоздание переиспользуемых объектов - ObjectMapper, DateTimeFormatter, Gson при каждом вызове
- Прикрепление виртуальных потоков (JDK 21-23) - synchronized + блокирующий ввод-вывод прикрепляет носители
Подробные примеры и исправления
1. Конкатенация строк в циклах
Проблемный код:
String report = "";
for (String line : logLines) {
report = report + line + "\n";
}Это создаёт O(n²) копирование из-за неизменяемости строк. Тесты BellSoft JMH показывают, что при увеличении n в 4 раза конкатенация в цикле замедляется более чем в 7 раз.
Исправление:
StringBuilder sb = new StringBuilder();
for (String line : logLines) {
sb.append(line).append("\n");
}
String report = sb.toString();Примечание: Начиная с JDK 9, компилятор оптимизирует однострочную конкатенацию, например "Order: " + id + " total: " + amount, но эта оптимизация не распространяется на циклы.
2. Случайная O(n²) с потоками внутри циклов
Проблемный код:
for (Order order : orders) {
int hour = order.timestamp().atZone(ZoneId.systemDefault()).getHour();
long countForHour = orders.stream()
.filter(o -> o.timestamp().atZone(ZoneId.systemDefault()).getHour() == hour)
.count();
ordersByHour.put(hour, countForHour);
}Этот паттерн составлял почти 71% образцов стека ЦП в записи JFR. При 10 000 заказах он выполняет 100 миллионов сравнений вместо одного прохода.
Исправление:
for (Order order : orders) {
int hour = order.timestamp().atZone(ZoneId.systemDefault()).getHour();
ordersByHour.merge(hour, 1L, Long::sum);
}Это обеспечивает производительность O(n) с одним проходом. Вы также можете использовать Collectors.groupingBy(... Collectors.counting()) в одном потоковом конвейере.
Статья является частью 1 из 3 частей серии по оптимизации производительности Java, части 2 и 3 скоро появятся. Часть 2 пройдёт через данные профилирования, стоящие за этими цифрами, включая flame graphs и то, какие методы действительно были горячими.
📖 Read the full source: HN AI Agents
👀 Смотрите также

Интерактивные объяснительные карты: проектирование циклов агентов Claude Code от одиночных вызовов до самомутирующих промптов
Интерактивный сайт, созданный с помощью Opus 4.7, визуализирует 11 реальных дизайнов циклов агентов для Claude Code: от базовых вызовов до агентов, которые переписывают собственные промпты, с SVG-анимациями, показывающими механизмы памяти и циклов.

Многоагентная архитектура: Избегание ловушки единого агента в системах искусственного интеллекта
В посте на Reddit указывается на распространенную архитектурную ошибку — использование одного агента для выполнения множества задач, что приводит к созданию хрупких систем, требующих постоянного контроля. Предлагаемое решение — модель «оркестратор-специалист», где каждый агент имеет узкую, конкретную роль.

Структурирование кодовых агентов Claude с помощью шаблонов CLAUDE.md и директории .claude/
Разработчик делится своим подходом к запуску нескольких ИИ-агентов с помощью Claude Code, где каждый агент имеет свою собственную директорию с файлом CLAUDE.md и директорией .claude/ с правилами и навыками. Ключевая идея заключается в разделении постоянно активного контекста и рабочих процессов по требованию для оптимизации использования токенов и качества ответов.

Понимание архитектуры ИИ-агентов: Детерминированные и вероятностные слои
Пользователь Reddit делится ментальной моделью для систем ИИ-агентов, которая разделяет детерминированные слои (скрипты, команды, API) и вероятностные слои (рассуждения и решения LLM). Ключевая идея: переносить как можно больше работы на детерминированную сторону.