Оптимизация производительности 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
👀 Смотрите также

Автостопом по Агентивному ИИ — Полный Обзор
Всеобъемлющий пост на Reddit, охватывающий принципы работы LLM, обучение с подкреплением для LLM, рассуждения, оценку, агентную память (RAG) и мультиагентные системы, со ссылкой на полную статью.

Как избежать непредвиденных расходов в OpenRouter при автоматизации OpenClaw
Команда разработчиков случайно потратила $750 за 3 дня на OpenRouter, используя по умолчанию Claude Sonnet 4.6 ($3/млн токенов) для всех автоматизированных задач. Они сократили расходы на 97%, изменив модели по умолчанию, зафиксировав cron-задачи и подзадачи на более дешёвых вариантах и оставив дорогие модели только для важной работы.

Оптимизация Qwen3.5-9B на RTX 3070 Mobile с помощью ik_llama.cpp: Настройки конфигурации и тесты производительности
Разработчик делится результатами оптимизации запуска модели Qwen3.5-9B Q4_K_M на ноутбуке с видеокартой RTX 3070 Mobile 8GB с использованием ik_llama.cpp, достигая скорости генерации ~50 токенов/сек и значительного улучшения оценки промптов за счёт настройки конфигурации.

OpenClaw 5.28: Плагин Codex сломан после обновления — исправление с помощью символической ссылки
OpenClaw 5.28 ломает плагин Codex из-за несоответствия пути к бинарнику. Исправление: создайте символическую ссылку с ожидаемого пути на фактический bin/codex.