← Назад к вопросам

Что происходит с объектами которые больше не нужны в Java

1.2 Junior🔥 172 комментариев
#JVM и память

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Механизм управления памятью и сборка мусора в Java

В Java управление памятью и удаление ненужных объектов осуществляется автоматически с помощью Garbage Collector (GC) — сборщика мусора. Это фундаментальное отличие от языков вроде C/C++, где программист должен явно освобождать память. Рассмотрим, что происходит с объектами, которые становятся недостижимыми.

Как объект становится "ненужным"?

Объект считается пригодным для удаления, когда на него нет достижимых ссылок. Это означает, что:

  • Все ссылки на объект вышли из области видимости (например, локальные переменные внутри метода).
  • Ссылки были переприсвоены другим объектам.
  • Объект был частью изолированного цикла ссылок, но ни один элемент этого цикла не достижим из "живых" корневых ссылок (корни GC – это статические поля, активные потоки, локальные переменные в стеке и т.д.).

Пример:

public class Example {
    public static void main(String[] args) {
        // 1. Создается объект, на него ссылается `obj1`
        Object obj1 = new Object();

        // 2. Создается второй объект, на него ссылается `obj2`
        Object obj2 = new Object();

        // 3. `obj2` начинает ссылаться на тот же объект, что и `obj1`.
        //    Первоначальный объект, созданный в строке 2, теперь НЕДОСТИЖИМ.
        obj2 = obj1;

        // 4. После завершения метода все локальные ссылки (`obj1`, `obj2`) уничтожаются.
        //    Оба созданных объекта становятся недостижимыми.
    }
}

Роль сборщика мусора (Garbage Collector)

Garbage Collector — это демон-поток JVM, который периодически запускается и выполняет три ключевые задачи:

  1. Marking (Пометка): GC проходит от корневых ссылок (GC Roots) и помечает все достижимые объекты как "живые". Всё, что не помечено, считается мусором.
  2. Deletion (Удаление): Классический алгоритм просто удаляет непомеченные объекты, освобождая их память.
  3. Compacting (Компактизация - опционально): После удаления фрагментация памяти может стать высокой. Некоторые сборщики (например, G1GC, ZGC) дополнительно перемещают "живые" объекты, чтобы собрать свободную память в один непрерывный блок, что ускоряет последующие аллокации.

Важно: Момент запуска GC недетерминирован и зависит от реализации JVM и алгоритма сборщика (Serial, Parallel, CMS, G1, ZGC). Сборка обычно инициируется при нехватке свободной памяти в куче (Heap) для выделения нового объекта.

Алгоритмы сборки мусора

В современной JVM (особенно в контексте Android, где долгое время использовалась Dalvik, а теперь ART) применяются сложные гибридные алгоритмы:

  • Generational Collection: Куча делится на молодое (Young) и старое (Old) поколения. Новые объекты создаются в Young Generation (в областях Eden и Survivor Spaces). Большинство объектов быстро умирают, поэтому Minor GC, очищающая только молодое поколение, выполняется часто и быстро. Выжившие объекты перемещаются в Old Generation, которую очищает более редкий и медленный Major GC (Full GC).
  • Concurrent Mark Sweep (CMS) / G1 / ZGC: Эти алгоритмы стремятся минимизировать STW (Stop-The-World) паузы, когда все потоки приложения останавливаются на время работы GC. Они выполняют значительную часть работы (например, пометку) параллельно с выполнением прикладного кода.

Метод finalize() и его опасности

До Java 9 у класса Object существовал метод protected void finalize(). Теоретически, GC вызывал его перед физическим удалением объекта, давая шанс освободить ресурсы (например, закрыть дескриптор файла). Практически его использование крайне не рекомендовалось, а теперь он депрекейтнут (помечен @Deprecated), потому что:

  • Время вызова негарантированно (объект может быть удален через минуту, час или никогда, если сборка не сработает).
  • Выполнение finalize() может воскресить объект, сделав его снова достижимым, что ломает логику GC.
  • Вызов finalize() происходит в отдельном потоке с непредсказуемым порядком, что порождает гонки данных и сложные для отладки ошибки.
  • Серьёзно снижает производительность сборки мусора.

Современная альтернатива — использование блоков try-with-resources для всего, что реализует интерфейс AutoCloseable.

// Правильный способ управлять ресурсами (не полагаясь на finalize)
try (FileInputStream fis = new FileInputStream("file.txt")) {
    // использовать ресурс
} catch (IOException e) {
    // обработка ошибки
}
// Ресурс fis.close() будет вызван АВТОМАТИЧЕСКИ здесь, детерминированно.

Заключение для Android

В Android (среда ART) также используется поколенческая сборка мусора с несколькими алгоритмами, оптимизированными для мобильных устройств с ограниченной памятью и батареей. Разработчику не нужно и не следует явно удалять объекты (например, присваивать null). Основная задача — избегать утечек памяти, которые возникают, когда ненужный объект неожиданно остаётся достижимым (через статическое поле, ссылку из активного потока, некорректную анонимную реализацию listener'a и т.д.). Для поиска таких утечек используются профилировщики памяти, например, Android Profiler в Android Studio.

Итог: Объекты, ставшие недостижимыми, помечаются сборщиком мусора как мусор и в неопределенный, но конечный момент времени будут автоматически удалены из памяти, освобождая ресурсы для работы приложения. Роль разработчика — писать код так, чтобы ссылки на объекты своевременно терялись, а не мешать работе GC.

Что происходит с объектами которые больше не нужны в Java | PrepBro