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

В какой момент происходит очистка Heap сборщиком мусора

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

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

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

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

# Очистка Heap и Garbage Collection в Java

Краткий ответ

Очистка Heap (удаление объектов, на которые нет ссылок) происходит во время GC паузы — специального момента, когда JVM приостанавливает выполнение приложения, запускает сборщик мусора, и затем возобновляет работу.

Когда именно запускается GC

Гарбадж Коллектор запускается в следующих случаях:

1. Когда Heap переполняется

Основная причина — недостаточно памяти для выделения нового объекта
public class GCTrigger {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        
        for (int i = 0; i < 1000; i++) {
            byte[] arr = new byte[1024 * 1024]; // 1 MB
            list.add(arr);
            // JVM начинает раздумывать о GC если Heap заканчивается
        }
    }
}

2. Явный вызов System.gc()

System.gc(); // просьба к JVM запустить GC (но это не гарантия!)

Важно: System.gc() — это только suggestion, а не команда. JVM может его проигнорировать.

3. Периодически (в зависимости от GC алгоритма)

Некоторые сборщики мусора работают по расписанию (например, G1GC).

Как происходит очистка (Mark-Sweep-Compact)

Фаза 1: Mark (Пометка)

GC идентифицирует объекты, на которые ещё есть ссылки:

public class ObjectReferences {
    public static void main(String[] args) {
        String active = "I'm alive"; // ссылка есть → будет помечен
        String garbage = new String("I'll be GC'd");
        garbage = null; // ссылка удалена → без пометки
        
        // Когда запустится GC:
        // - active будет помечен как "живой"
        // - garbage будет помечен как "мусор"
    }
}

Фаза 2: Sweep (Удаление)

Удаляются объекты без ссылок:

До GC:  [obj1][obj2_garbage][obj3][obj4_garbage][obj5]
         Mark: ✓          ✗         ✓        ✗         ✓
         
После:  [obj1][obj3][obj5]

Фаза 3: Compact (Дефрагментация)

Оставшиеся объекты сдвигаются, чтобы избежать фрагментации:

// Это бывает не в очень сборщике (например, CMS skip compact)
public class CompactionExample {
    // До:  [obj1_small] [gap] [obj2] [gap] [obj3]
    // После: [obj1] [obj2] [obj3] [free_space_for_allocation]
}

GC Pause

Во время сборки мусора всё приложение останавливается:

Время обычно: 1-100ms (зависит от количества мусора и размера Heap)

Примечание: это критично для низколатентных систем (трейдинг, реалтайм)
public class GCPauseMeasure {
    public static void main(String[] args) throws Exception {
        long start = System.nanoTime();
        
        // создаём мусор
        List<byte[]> trash = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            trash.add(new byte[1024]);
        }
        trash.clear();
        
        // GC может запуститься здесь (pause)
        Thread.sleep(100); // имитируем работу
        
        long elapsed = System.nanoTime() - start;
        System.out.println("Elapsed: " + elapsed);
    }
}

Разные сборщики мусора и их поведение

Serial GC

Откл: -XX:+UseSerialGC
Процесс: Single-threaded, паузы могут быть долгие

Parallel GC (default до Java 9)

Откл: -XX:+UseParallelGC
Процесс: Multi-threaded Mark-Sweep-Compact
Оптимизация: для throughput (пропускная способность)

G1GC (default с Java 9)

Откл: -XX:+UseG1GC
Процесс: Incremental, разбивает Heap на регионы
Оптимизация: низколатентные паузы (predictable latency)

ZGC / Shenandoah (Java 15+)

Откл: -XX:+UseZGC / -XX:+UseShenandoahGC
Процесс: Concurrent (не останавливает приложение полностью)
Оптимизация: ultra-low latency, до 10ms паузы

Мониторинг GC

# Включить логирование GC
java -Xlog:gc* MyApp

# Или старый стиль
java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps MyApp

Лучшие практики для минимизации GC

// 1. Избегай создания излишних объектов
public class ObjectCreation {
    // ❌ Плохо — создаёт объект в каждом вызове
    public String buildString(String a, String b) {
        return new String(a + b); // новый объект!
    }
    
    // ✅ Хорошо
    public String buildString(String a, String b) {
        return a.concat(b); // может быть оптимизировано
    }
}

// 2. Используй Object Pool для критичных операций
public class ObjectPool {
    private Queue<ByteBuffer> pool = new LinkedList<>();
    
    public ByteBuffer acquire() {
        return pool.poll() != null ? pool.poll() : ByteBuffer.allocate(1024);
    }
    
    public void release(ByteBuffer buffer) {
        buffer.clear();
        pool.offer(buffer);
    }
}

// 3. Правильно задавай Heap размер
// -Xms1024m -Xmx1024m (одинаковый размер = меньше пауз)

Вывод

Очистка Heap происходит во время GC паузы, которая запускается когда:

  1. Heap переполняется
  2. Явный вызов System.gc()
  3. По расписанию (зависит от GC алгоритма)

В этот момент JVM:

  • Останавливает приложение
  • Помечает живые объекты (Mark)
  • Удаляет мусор (Sweep)
  • Дефрагментирует память (Compact)
  • Возобновляет работу

Для латентно-критичных приложений используй современные сборщики (ZGC, Shenandoah).