Главная Веб-разработка Cборка мусора в Java Highload

Cборка мусора в Java Highload

от admin

Проектирование Java Highload приложения с высоким SLA по RPS и latency затрагивает множество аспектов. В частности, OLTP, мультиплексирование запросов, неблокирующий I/O и прочее. В данной статье хотел бы поговорить про сборку мусора.

1125 открытий750 показов

Несколько лет работаю Java инженером в финтехе. Недавно вывели на рынок очередное HL-решение со строгим SLA по RPS и Latency. Хотел бы кратко описать проблемы и путь их решения.

Когда бизнес затребовал систему для обработки транзакций в реальном времени с пиковой нагрузкой в 50 тысяч операций в секунду, я понял, что серые будни enterprise инженера наконец станут весельем. Наш сервис должен был не просто работать — он должен был дышать под давлением, сохраняя отклик в пределах 10 мс. Не все проблемы в мире можно решить горизонтальным масштабированием, и одним из камней преткновения стала Java, а точнее — её «мусор».

Выбор GC: G1, Shenandoah или ZGC?

Первое, с чем столкнулась наша команда, — продолжительные STW-прерывания при нагрузке. Изначально использовали G1GC как «дефолтный» выбор для баланса между latency и throughput. Но под нагрузкой 80% CPU даже G1 не справлялся: пиковые паузы достигали 400 мс, что для платежей было неприемлемо.

Мы устроили мозговой штурм с performance-инженерами. Варианты:

Скрутка и накрутка опыта: работает ли это в айтишкеtproger.ru

1. Shenandoah — низкие паузы, но требуется больше CPU.

2. ZGC — субмиллисекундные паузы даже на терабайтных хипах, но тогда (на старте проекта) он был экспериментальным в OpenJDK 11.

3. Ручная настройка G1 — максимизировать предсказуемость через MaxGCPauseMillis и InitiatingHeapOccupancyPercen.

После недели тестов на стенде с имитацией пиковой нагрузки (JMeter + Gatling) и профилирования через async-profiler, выбор пал на ZGC. Его паузы не превышали 2 мс даже при 32 ГБ куче, а алгоритм «цветных указателей» позволял масштабироваться без блокировок. Риск? Да. ZGC был новым, но его поддержка в последних LTS-версиях и тесты убедили нас.

Тонкая настройка и проклятие «тихих» утечек

С ZGC мы выставили:

1. -Xmx48g (с запасом для избежания частых аллокаций),

2. -XX:SoftMaxHeapSize=40g (чтобы ZGC активнее возвращал память ОС),

Читать также:
Управляющие конструкции в программировании - Как работают if, else, switch, for, while - Tproger

3. -XX:+UseLargePages (снижение overhead на page faults).

Что значит быть инженером в новых реалиях? И какой смысл мы вкладываем в эти слова — расскажем на GPB CONF!tproger.ru

Но через месяц нагрузочного тестирования обнаружили «ползучее» увеличение потребления памяти. Performance-команда, анализируя дампы через Eclipse MAT, нашла проблему: кэш данных транзакций в Apache Ignite не учитывал soft-ссылки, накапливая объекты. Вместо ConcurrentHashMap перешли на `Caffeine` с политикой expiry после 10 секунд, что снизило давление на GC.

Команда performance-инженеров: алхимики метрик

Работа с perfomance-инженерами напоминала лабораторию безумных ученых. Каждый параметр JVM, каждый HTTP-эндпоинт тестировался через JMH, а Grafana-дашборды визуализировали следующее:

1. GC pauses (ZGC собирал статистику через -Xlog:gc*),

2. Allocation rate (до 1.5 ГБ/сек в пиках),

3. CPU utilization (наши самописные RateLimiter’ы съедали 15% ресурсов).

Совместно мы написали скрипты на Python, которые автоматически подбирали -XX:ConcGCThreads и -XX:ParallelGCThreads в зависимости от нагрузки на CI/CD стендах. Это сократило время настройки на 70%.

Продакшн: огненное крещение

После релиза первые дни мы мониторили всё: New Relic для трейсинга медленных методов, Prometheus для сбора JVM-метрик. ZGC не подвел — 99-й перцентиль отклика оставался на уровне 12 мс. Но однажды ночью мы получили алерты по потреблению CPU. Оказалось, фоновый процесс генерации отчетов создавал миллионы временных объектов. Решение: выделили его в отдельный микросервис с выделенным пулом памяти и CMS (ему хватало).

Итоги

Через полгода система обрабатывала 55k операций/сек с пиковыми паузами GC в 1.8 мс. Главные уроки:

1. GC — не серебряная пуля. Даже ZGC требует понимания аллокаций в коде.

2. Performance-инженеры — ваши лучшие союзники. Их взгляд со стороны JVM спас нас десятки раз.

3. Документировать всё. Наши конфиги и скрипты настройки GC стали стандартом для других команд банка.

Теперь, когда я вижу, как сервис работает без перебоев, вспоминаю те бессонные недели с дампами и тестами… и понимаю: это того стоило. Ведь в мире highload каждая миллисекунда — это деньги. Или, как говорят у нас в банке, «миллисекунда — миллион».

Похожие статьи