GC Roots: почему Java не удаляет объект, которым мы больше не пользуемся
В прошлом посте разобрали важный момент:
-Xmx2g ограничивает Heap, но память JVM не заканчивается на Heap.
Теперь разберёмся, как JVM вообще решает, какие объекты можно удалить.
Представим:
public void process() {
User user = new User("Alex");
}
Метод завершился, переменная user больше не существует и кажется, объект можно удалить, и действительно — если на User больше никто не ссылается, GC сможет его собрать.
Но важно понимать:
GC не смотрит на то, нужна ли переменная, а смотрит на достижимость объекта.
Если упрощённо JVM начинает поиск с особых объектов — GC Roots:
GC Roots
│
├── локальные переменные активных методов
├── статические поля
├── активные Java-потоки
└── другие специальные ссылки JVM
│
↓
Object A
│
↓
Object B
Если до объекта можно добраться от GC Root — объект считается живым.
Если добраться нельзя — объект становится кандидатом на сборку.
Но теперь интересное.))
Представим:
public static List<User> users = new ArrayList<>();
И где-то в коде:
users.add(new User("Alex"));
Через неделю этот пользователь нам уже вообще не нужен, однако его забыли удалить из списка.
Что происходит?
GC Root
↓
static users
↓
ArrayList
↓
User
Объект всё ещё достижим и для GC он жив.
Тут совершенно неважно, что бизнес-логике этот User больше не нужен.
Если продолжать добавлять объекты:
users.add(...)
users.add(...)
users.add(...)
...
коллекция будет расти.
И в какой-то момент мы получаем: OutOfMemoryError
Это один из классических вариантов memory leak в Java.
И здесь важный нюанс:
Memory leak в Java — это не обязательно потерянная память.
Часто это память, которую GC технически может освободить, но не имеет права, потому что объект всё ещё достижим.
Поэтому при расследовании проблем с памятью недостаточно смотреть:
"У нас много объектов"
Нужно задать другой вопрос:
«Почему эти объекты всё ещё достижимы от GC Roots?»
Именно поэтому при анализе Heap Dump мы ищем не только самые большие объекты, но и цепочку их удержания — Path to GC Roots.
И вот здесь JVM становится действительно интересной:
иногда проблема не в том, что приложение создаёт слишком много объектов.
Проблема в том, что оно слишком хорошо умеет их не удалять.