Оптимизация производительности 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 O365 MCP
Разработчик делится конкретными решениями для двух проблем, возникших при настройке O365 MCP коннектора Claude Code в условиях политик условного доступа: поиск правильных идентификаторов приложений для правил политик и устранение ошибок аутентификации, связанных с местоположением серверов.

Как праздный агент сжигал 50 млн токенов в день – и как это исправить
Простаивающий агент OpenClaw сжег 50M токенов в день из-за heartbeat-пингов с раздутой сессией. Пользователь Reddit рассказывает, как отследил утечку и исправил её настройками конфигурации.

Контрактное тестирование для разработки на основе искусственного интеллекта с использованием OpenClaw
Контрактное тестирование может заменить интеграционные/E2E-тесты при использовании ИИ-агентов, таких как OpenClaw, фокусируясь на интерфейсах и инвариантах между компонентами. ИИ генерирует код для удовлетворения детерминированных контрактов, создавая быструю обратную связь для ускорения итераций.

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