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

Как определить объекты, подлежащие удалению сборщиком мусора?

1.7 Middle🔥 161 комментариев
#JVM и управление памятью

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

# Определение объектов для удаления сборщиком мусора

Сборщик мусора (Garbage Collector) использует различные алгоритмы для определения объектов, которые больше не используются. Основной подход — анализ достижимости объектов.

Концепция Reachability (Достижимость)

Объект подлежит удалению, если он недостижим из корневых ссылок (root references). Корневыми ссылками являются:

// 1. Локальные переменные в активных потоках
public void method() {
    Object obj = new Object(); // obj является корневой ссылкой
    // obj достижим в этом методе
} // после выхода из метода obj может быть удалён

// 2. Статические переменные класса
public class DataHolder {
    public static List<String> data = new ArrayList<>(); // всегда достижима
}

// 3. Активные потоки
Thread thread = new Thread(() -> {
    Object obj = new Object(); // достижим через поток
});

// 4. Объекты в памяти JVM (классы, монитор locks)
public class Monitor {
    synchronized void method() { // монитор объекта достижим
        // ...
    }
}

Алгоритмы определения недостижимых объектов

1. Mark and Sweep (Пометь и очисти)

Самый распространённый алгоритм:

// Фаза Mark (Пометка)
public class MarkAndSweep {
    // Обходим граф объектов, помечая достижимые
    private void markReachable(Object obj, Set<Object> marked) {
        if (marked.contains(obj)) return;
        marked.add(obj);
        
        // Рекурсивно помечаем все ссылаемые объекты
        for (Object reference : obj.getReferences()) {
            markReachable(reference, marked);
        }
    }
    
    // Фаза Sweep (Очистка)
    private void sweep(Set<Object> marked) {
        for (Object obj : allObjects) {
            if (!marked.contains(obj)) {
                delete(obj); // удаляем недостижимые объекты
            }
        }
    }
}

Процесс:

  1. GC приостанавливает все потоки (Stop The World)
  2. Обходит все объекты, достижимые из root references
  3. Помечает их как живые
  4. Удаляет все непомеченные объекты
  5. Возобновляет работу потоков

2. Generational Hypothesis

Модель поколений основана на предположении, что молодые объекты часто умирают:

public class GenerationalGC {
    // Young Generation — часто сканируется
    private void minorGC() {
        // GC часто проверяет молодое поколение
        Object young = new Object(); // создан в Young Gen
        // используется...
        // удаляется при Minor GC
    }
    
    // Old Generation — редко сканируется
    private void majorGC() {
        Object old = new Object();
        // пережил несколько Minor GC
        // переместился в Old Gen
        // удаляется при Major GC (реже)
    }
}

3. Reference Counting (менее часто используется)

public class RefCountExample {
    // Каждый объект имеет счётчик ссылок
    static class RefCountedObject {
        int refCount = 0; // количество ссылок
        
        void addRef() {
            refCount++;
        }
        
        void removeRef() {
            refCount--;
            if (refCount == 0) {
                delete(this); // удаляем, когда нет ссылок
            }
        }
    }
}

Практический пример

public class GCExample {
    static class Node {
        Node next;
        String data;
        
        Node(String data) {
            this.data = data;
        }
    }
    
    public static void main(String[] args) {
        // 1. Создаём объекты (достижимы)
        Node node1 = new Node("First");
        Node node2 = new Node("Second");
        node1.next = node2;
        
        System.out.println("Created: " + node1.data);
        
        // 2. Удаляем ссылку (node2 всё ещё достижима через node1)
        // (ничего не удаляется)
        
        // 3. Удаляем node1 (обе цепочка становится недостижима)
        node1 = null;
        // node1 и node2 теперь доступны для удаления GC
        
        // 4. В правильный момент GC удалит оба объекта
        System.gc(); // явный вызов (не гарантирует выполнение)
    }
}

Слабые ссылки (Weak References)

Для более гибкого контроля над GC используются слабые ссылки:

import java.lang.ref.WeakReference;

public class WeakRefExample {
    static class Cache {
        // Объект может быть удалён GC, даже если есть WeakReference
        private Map<String, WeakReference<String>> cache = new HashMap<>();
        
        public void put(String key, String value) {
            cache.put(key, new WeakReference<>(value));
        }
        
        public String get(String key) {
            WeakReference<String> ref = cache.get(key);
            if (ref != null) {
                String value = ref.get(); // может быть null, если GC удалил
                return value;
            }
            return null;
        }
    }
}

Типы ссылок в Java:

  • Strong Reference — обычные ссылки, объект не удаляется, пока на него есть strong reference
  • Soft Reference — объект удаляется если память низкая
  • Weak Reference — объект удаляется при следующем GC
  • Phantom Reference — для cleanup операций перед удалением

Как GC определяет объект для удаления

  1. Нет достижимых путей от root references
  2. Не находится в памяти активной части программы
  3. Все ссылки на объект weak или phantom (при условиях)
  4. Нет блокировок или мониторов на объекте

Мониторинг GC

# Флаги для мониторинга
java -Xmx1024m -Xms512m \
     -XX:+PrintGCDetails \
     -XX:+PrintGCDateStamps \
     -Xloggc:gc.log \
     MyApplication

Заключение

Сборщик мусора Java автоматически отслеживает доступность объектов и удаляет недостижимые. Понимание этого процесса важно для написания эффективного кода и избегания утечек памяти через различные типы ссылок.