Сначала стоит сказать, что это такое.
Утечка памяти (memory leak) в широком смысле – потеря доступа к некоторой сущности, которая при этом всё еще остается «живой» и расходует ресурсы компьютера.
Основное отличие Java от языков вроде C – автоматическое управление памятью. В общем случае вам не нужно думать об удалении объекта из памяти. Когда он перестал быть нужен, сборщик мусора сделает это за вас.
Но всё-таки бывают случаи, когда JVM не способна помочь, и прибираться за собой нужно вручную:
1. Объекты в статических полях. Обычно они живут пока живёт класслоадер, который обычно живет до конца работы приложения. В эту же группу риска попадают синглтоны, которые обычно базируются на статиках.
Отдельного внимания заслуживает случай утечки
ThreadLocal
.
2. Взаимодействие с нативным кодом и ручное управление памятью. Когда вы решаетесь на ручное/внешнее управление, вся ответственность за сборку мусора переходит на вас. Это касается использования
Unsafe
и нативных библиотек. Сюда же попадают различные утечки внешних ресурсов: например соединений с базой через нативный драйвер.
3. Неправильное использование коллекций. Несогласованность методов
equals-hashCode может позволить ключам теряться внутри
HashMap
/
HashSet
. Размер зарезервированной памяти часто не совпадает с размером содержимого: тот же
HashMap
, однажды раздувшись, не умеет уменьшаться.
4. Использование
finalize. Вмешиваясь в нормальную работу GC, вы, естественно, можете её нарушить. Поток финализации имеет низкий приоритет – даже корректная реализация метода может не успеть выполниться и привести к
OutOfMemoryError
.
5. Утечка
inner-класса. В отличие от nested, inner класс содержит неявную ссылку на своего хозяина. Так что экземпляр хозяина гарантированно будет жить пока живут экземпляры его inner-классов.
6. Интернированные строки. Вызовом
String.intern()
вы подписываетесь под тем, что осознаете что делаете. Ручное использование оптимизационных хитростей JVM не может не сопровождаться риском. Поведение этого метода
зависит от версии Java и реализации JVM.
7. Паттерн Flyweight/object pool. И пул строк, и вся модель памяти реализуют его. Неправильная программная реализация паттерна также может привести к утечке объектов – зависанию их в пулле без реальных применений снаружи.
8. Всевозможные логические утечки. Нарушение консистентности модели бизнес-данных может приводить к забытым объектам. Технически это всё еще один из пунктов выше. Типичный пример такой утечки –
утечка Activity в Android.
Так что ответ на вопрос – редко, но
бывают.