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

Как ненужные объекты удаляются в программе

2.0 Middle🔥 151 комментариев
#ООП#Основы Java

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

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

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

Как ненужные объекты удаляются в программе (Garbage Collection)

Удаление неиспользуемых объектов в Java происходит автоматически через процесс сборки мусора (Garbage Collection). Это один из самых важных механизмов JVM.

1. Основной концепт: Mark and Sweep

Шаг 1: MARK (Пометка)
GC сканирует объекты из корневых ссылок (stack, static fields)
Помечает все объекты, которые достижимы (reachable)

    GC Root         
       │
       ├─→ Object A ✓ (в памяти)
       │     │
       │     └─→ Object B ✓ (в памяти)
       │
       └─→ Object C ✓ (в памяти)

Объект D ✗ (недостижим → кандидат на удаление)

Шаг 2: SWEEP (Очистка)
Udeletes все неповеченные объекты
Освобождает память для новых объектов

2. Корневые ссылки (GC Roots)

public class GCRootsExample {
    
    // 1. Static references — живут столько же, сколько класс
    static Object staticRef = new Object();
    
    public static void example() {
        // 2. Stack local variables
        Object localRef = new Object();
        
        // 3. Active thread
        Thread thread = new Thread(() -> {
            Object threadLocalRef = new Object();
            // Живет пока живет поток
        });
    }
    
    // 4. JNI references (native код)
    // 5. Method arguments
    public void methodWithArg(Object arg) {
        // arg живет пока выполняется метод
    }
}

public class ReachabilityExample {
    public static void main(String[] args) {
        // Object A достижим из stack
        Object a = new Object();
        
        // Object B достижим из a
        Object b = a;
        
        // Object C недостижим после удаления ссылки
        Object c = new Object();
        c = null;  // Теперь c кандидат на GC
        
        // После выхода из метода все локальные объекты удаляются
    }
}

3. Жизненный цикл объекта

┌──────────────────────────────────────────────────────┐
│  ОБЪЕКТ СОЗДАН: new Object()                         │
│  ↓                                                    │
│  МОЛОДОЕ ПОКОЛЕНИЕ (Young Generation)               │
│  ├─ Eden Space: новые объекты                       │
│  ├─ Survivor Space 0: выжившие объекты              │
│  └─ Survivor Space 1: выжившие объекты              │
│     ↓ (после нескольких Minor GC)                   │
│  СТАРОЕ ПОКОЛЕНИЕ (Old Generation)                  │
│  ├─ долгоживущие объекты (пережившие 15 GC)        │
│  └─ Full GC удаляет мертвые объекты                │
│     ↓                                                 │
│  УДАЛЕН (освобождена память)                        │
└──────────────────────────────────────────────────────┘

4. Типы Garbage Collection

// 1. MINOR GC (Young Generation collection)
// Быстро, происходит часто (~100ms)
// Удаляет короткоживущие объекты

// 2. MAJOR/FULL GC (Old Generation collection) 
// Медленно, происходит редко (~1s)
// ВАЖНО: останавливает все потоки приложения

@Service
public class GCMonitoringService {
    
    private final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    private final GarbageCollectorMXBean[] gcBeans = 
        ManagementFactory.getGarbageCollectorMXBeans();
    
    public void monitorGC() {
        for (GarbageCollectorMXBean bean : gcBeans) {
            long collectionCount = bean.getCollectionCount();
            long collectionTime = bean.getCollectionTime();
            
            log.info("GC: {} - Count: {}, Time: {}ms", 
                bean.getName(), collectionCount, collectionTime);
            
            // G1GC, CMS, Parallel GC имеют разные стратегии
            if ("G1 Young Generation".equals(bean.getName())) {
                log.info("Young Gen Minor GC: {} collections", collectionCount);
            }
        }
    }
}

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

import java.lang.ref.*;

public class WeakReferenceExample {
    
    public static void demonstrateWeakReferences() {
        // Strong reference
        Object strongRef = new Object();
        
        // Weak reference — может быть удален при любом GC
        WeakReference<Object> weakRef = new WeakReference<>(strongRef);
        
        // Можем проверить, жив ли объект
        if (weakRef.get() != null) {
            System.out.println("Object still alive");
        }
        
        // После удаления strong reference, объект может быть удален
        strongRef = null;
        System.gc(); // Suggestion to GC
        
        if (weakRef.get() == null) {
            System.out.println("Object was garbage collected");
        }
    }
    
    // Применение: кэши где нужна гибкость
    @Service
    public class WeakCacheService {
        
        // Если памяти мало — значения удаляются
        private final Map<String, WeakReference<ExpensiveObject>> cache = 
            new WeakHashMap<>();
        
        public ExpensiveObject getOrCompute(String key) {
            WeakReference<ExpensiveObject> ref = cache.get(key);
            
            if (ref == null || ref.get() == null) {
                ExpensiveObject value = computeExpensive(key);
                cache.put(key, new WeakReference<>(value));
                return value;
            }
            
            return ref.get();
        }
    }
}

6. Мягкие ссылки (Soft References) для кэширования

public class SoftReferenceCache {
    
    // Используется перед OutOfMemoryError
    // Идеально для кэшей
    private final Map<String, SoftReference<byte[]>> imageCache = 
        new HashMap<>();
    
    public byte[] getImage(String key) {
        SoftReference<byte[]> ref = imageCache.get(key);
        
        if (ref == null || ref.get() == null) {
            // Нужно пересчитать
            byte[] data = loadImageFromDisk(key);
            imageCache.put(key, new SoftReference<>(data));
            return data;
        }
        
        return ref.get();
    }
    
    // Приложение может работать с кэшем на 80% меньше памяти
    // Но когда требуется — объекты сохраняются
}

7. Phantom References для cleanup

public class PhantomReferenceExample {
    
    // Используется только для cleanup действий
    private final ReferenceQueue<Resource> referenceQueue = new ReferenceQueue<>();
    
    public void trackResource(Resource resource) {
        PhantomReference<Resource> phantomRef = 
            new PhantomReference<>(resource, referenceQueue);
        
        // Запускаем поток для cleanup
        new Thread(() -> {
            while (true) {
                try {
                    PhantomReference<?> ref = 
                        (PhantomReference<?>) referenceQueue.remove();
                    if (ref != null) {
                        log.info("Resource was garbage collected, cleaning up");
                        // Закрываем ресурсы
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }).start();
    }
}

8. Явное управление памятью (редко необходимо)

public class MemoryManagementExample {
    
    @Service
    public class LargeDataProcessor {
        
        public void processLargeDataset() {
            byte[] largeArray = new byte[100_000_000]; // 100 MB
            
            try {
                // Обработка данных
                processData(largeArray);
            } finally {
                // Явно освобождаем память
                largeArray = null;
                
                // Подсказка JVM запустить GC
                // НЕ ПОЛАГАЙСЯ НА ЭТО В PRODUCTION!
                // System.gc();
            }
        }
        
        // Лучший подход: используй try-with-resources
        public void processFileUsingTryWithResources(String filename) {
            try (FileInputStream fis = new FileInputStream(filename);
                 BufferedInputStream bis = new BufferedInputStream(fis)) {
                // Файл автоматически закроется и будет удален GC
                byte[] data = bis.readAllBytes();
                process(data);
            } catch (IOException e) {
                log.error("Error processing file", e);
            }
        }
    }
}

9. GC Flags для оптимизации

# Запуск с логированием GC
java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log -jar app.jar

# Выбор GC алгоритма
java -XX:+UseG1GC -jar app.jar                    # G1 (по умолчанию в Java 9+)
java -XX:+UseParallelGC -jar app.jar             # Parallel GC
java -XX:+UseConcMarkSweepGC -jar app.jar        # CMS GC (deprecated)

# Параметры памяти
java -Xms1g -Xmx2g -jar app.jar                  # Heap: 1GB start, 2GB max
java -XX:NewRatio=2 -jar app.jar                 # Young:Old = 1:2
java -XX:SurvivorRatio=8 -jar app.jar            # Eden:Survivor = 8:1

10. Диагностика проблем GC

@Component
public class MemoryLeakDetector {
    
    private static final long THRESHOLD = Runtime.getRuntime().maxMemory() * 90 / 100;
    
    @Scheduled(fixedRate = 60000) // каждую минуту
    public void checkMemoryUsage() {
        long usedMemory = Runtime.getRuntime().totalMemory() - 
                         Runtime.getRuntime().freeMemory();
        
        if (usedMemory > THRESHOLD) {
            log.warn("Memory usage is high: {}MB / {}MB", 
                usedMemory / 1024 / 1024,
                Runtime.getRuntime().maxMemory() / 1024 / 1024);
            
            // Возможные утечки памяти!
            // Проверь:
            // 1. Растущие коллекции
            // 2. ThreadLocal переменные
            // 3. Статические ссылки
            // 4. Неправильно закрытые ресурсы
        }
    }
    
    public void dumpHeap() {
        try {
            HotSpotDiagnosticsMXBean mxBean = ManagementFactory
                .getPlatformMXBean(HotSpotDiagnosticsMXBean.class);
            mxBean.dumpHeap("/tmp/heap.bin", true);
            log.info("Heap dumped to /tmp/heap.bin");
        } catch (IOException e) {
            log.error("Failed to dump heap", e);
        }
    }
}

Итоговая таблица типов ссылок

ТипУдаляетсяПрименение
StrongПри отсутствии ссылокОбычные объекты
WeakПри любом GCКэши, слабые ассоциации
SoftПеред OutOfMemoryАгрессивное кэширование
PhantomПосле удаленияCleanup, финализация

Гарбейдж Коллекшн — это автоматический механизм, но понимание как он работает критично для написания эффективного кода.

Как ненужные объекты удаляются в программе | PrepBro