
В процессе перехода с JDK 8 на JDK 17 команда Spring АйО столкнулась с серьезными проблемами, связанными с утечкой памяти. Миграция прошла успешно на этапе разработки и тестирования, но в продуктивной среде уже через 2-3 часа начались сбои.
- Увеличение потребления памяти в 4 раза
- Контейнеры начали перезапускаться из-за OOMKill
- Количество потоков возросло с 400 до более 1600
- Нативная память увеличилась с 800 MB до 3,6 GB
Причины проблем с памятью
Проблема заключалась в нескольких аспектах, усилившихся в контейнеризованной среде:
- Неверное определение CPU: JVM игнорировала лимиты CPU в cgroup, что приводило к созданию избыточного количества потоков.
- Фрагментация malloc-арен: Из-за увеличения количества потоков, glibc создавала слишком много malloc-арен, что увеличивало использование памяти.
- Накладные расходы G1GC: Переход на G1GC увеличил потребление нативной памяти.
Исправления и рекомендации
После тщательного анализа команда внедрила несколько исправлений:
| Исправление | Описание |
|---|---|
| Установка количества CPU | -XX:ActiveProcessorCount=2 |
| Ограничение malloc-арен | export MALLOC_ARENA_MAX=2 |
| Настройка или замена G1GC | Использование ParallelGC или настройка G1GC |
После этих изменений использование памяти стабилизировалось на уровне 65-70%. Команда подчеркнула важность мониторинга как нативной, так и heap-памяти, чтобы избежать подобных инцидентов в будущем.
Эти уроки подчеркивают, что миграция JVM требует тщательного планирования и анализа, что важно учитывать при обновлении до новых версий Java.