← Назад к вопросам
Как ненужные объекты удаляются в программе
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, финализация |
Гарбейдж Коллекшн — это автоматический механизм, но понимание как он работает критично для написания эффективного кода.