tg-me.com/javatg/1864
Last Update:
☕ Задача для опытных Java-разработчиков: «Призрачная утечка памяти в продакшене»
🧠 Уровень: Senior Java / JVM Internals
🎯 Цель: Найти источник утечки памяти в долгоживущем Java-сервисе и объяснить поведение GC
📍 Ситуация:
У вас есть Java-сервис (Spring Boot), который стабильно работает 5–6 часов, а потом начинает резко тормозить.
Мониторинг показывает рост heap usage почти до лимита, несмотря на регулярные GC-сборки.
Интересные факты:
- GC срабатывает часто, но не очищает больше 20–30% хипа
- Не происходит OutOfMemoryError
, но приложение начинает "фризить"
- Подозрения падают на кэш, но размер кэша — ограничен (Caffeine, maxSize=5000)
- Все коллекции под контролем, слабые ссылки используются
- Нет утечек в логах, нет Thread`-leak по `jstack
🧩 Задача:
1. Где искать утечку, если GC работает, но память не освобождается?
2. Почему слабые ссылки не помогают?
3. Как поведение ClassLoader
может влиять на утечку?
4. Какой инструмент использовать для анализа "живых" объектов в памяти?
5. Как бы вы реализовали периодический аудит памяти в проде без перезапуска?
6. Покажи, как сработает jmap
, jcmd
, MAT
или VisualVM
для выявления источника
💡 Подсказка:
Некоторые библиотеки (особенно с рефлексией, динамической загрузкой классов и кэшем) создают утечки через неудаляемые ссылки на ClassLoader, особенно в сочетании с ThreadLocal
, ScheduledExecutorService
, Class.forName
, URLClassLoader
.
🛠 Решение:
1. Снимаем дамп памяти:
jmap -dump:format=b,file=heap.bin <PID>
или
jcmd <PID> GC.heap_dump heap.bin
2. Анализируем в Eclipse MAT:
- Сортируем объекты по retained size
- Видим
ThreadLocalMap
, ClassLoader
, Class
→ strong references остаются даже после GC- Причина — статические поля/синглтоны держат ссылки на классы, которые не выгружаются
3. Проверяем код:
- Использовался
Class.forName(...)
в плагин-системе- Загруженные классы не выгружались, т.к. на них ссылается пул потоков
4. Решения:
- Использовать
WeakReference
к загруженным классам- Вызывать
shutdownNow()
и вручную очищать ThreadLocal.remove()
- Избегать глобальных синглтонов со ссылками на классы, загружаемые динамически
5. Профилактика:
- Включить периодический
jcmd GC.class_histogram
- Встроить
/heapdump
endpoint для отладки в проде (под защищённым доступом)📌 Вывод:
Даже при работающем GC утечка возможна, если в памяти удерживаются ссылки через
ThreadLocal
, ClassLoader
, или пула потоков. Это не очевидно и не вызывает OOM, но "раздувает" хип и убивает производительность.💬 Идеальный вопрос для собеседования на позицию Java-сеньора с уклоном в JVM-интерны.
@javatg
BY Java
Warning: Undefined variable $i in /var/www/tg-me/post.php on line 283
Share with your friend now:
tg-me.com/javatg/1864