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

Когда происходит зачистка кучи?

2.0 Middle🔥 111 комментариев
#JavaScript Core

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

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

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

Зачистка кучи в JavaScript

Зачистка кучи (heap cleanup) в контексте JavaScript — это процесс, который выполняется сборщиком мусора (Garbage Collector, GC) автоматически для освобождения памяти, занятой объектами, которые больше не используются в программе.

Когда происходит зачистка?

Зачистка кучи не происходит по расписанию или в строго определенные моменты. Это недетерминированный процесс, управляемый движком JavaScript (например, V8 в Chrome и Node.js). Однако можно выделить ключевые условия и триггеры:

  1. Когда память подходит к лимиту: Движок старается освобождать память по мере необходимости. Если свободной памяти становится мало для выполнения новых операций (например, создания объекта или строки), GC может запуститься.
  2. В моменты "пауз" или "тишины": Современные движки стараются выполнять сборку мусора преимущественно во время периодов низкой активности, чтобы минимизировать влияние на производительность (используя инкрементальную и параллельную сборку). Но это не всегда возможно.
  3. При выполнении некоторых явных операций:
    *   В старых версиях IE вызов `CollectGarbage()` мог спровоцировать сборку (нестандартный метод).
    *   При присвоении `null` или перезаписывании ссылок на крупные объекты (например, массивы или объекты) движок может быстрее определить их как "мертвые" и освободить память в следующем цикле GC.
  1. По завершении выполнения функций: Локальные переменные функции становятся недоступны после её выполнения и могут быть очищены, если на них нет внешних ссылок.

Как работает сборщик мусора в V8 (пример для современных движков)

Основной принцип — определение недостижимых объектов (reachability). Если объект больше не может быть достигнут из корня (global object, активный контекст выполнения), он считается мусором.

// Пример создания и "умирания" объекта
function createUser() {
    let user = { name: "John", data: new Array(1000000) }; // 1. Объект создается в куче
    return user;
}

let currentUser = createUser(); // 2. Ссылка на объект сохраняется в переменной

currentUser = null; // 3. Ссылка уничтожена. Огромный объект и внутренний массив теперь НЕДОСТИЖИМЫ.
// Они становятся кандидатами на зачистку в куче.

В V8 используется сложная система с маркировкой и очисткой (mark-and-sweep), генерационной сборкой и инкрементальной сборкой (Incremental GC).

Генерационный подход делит объекты на две категории:

  • Young Generation: Новые объекты. Сборка здесь происходит часто и быстро (scavenge).
  • Old Generation: Объекты, которые прожили несколько циклов. Сборка здесь более затратная и происходит реже.

Пример с циклическими ссылками, которые современные GC могут обрабатывать:

// Циклическая ссылка не помешает сборке, если объекты недостижимы из корня
let node1 = { next: null };
let node2 = { next: null };
node1.next = node2;
node2.next = node1; // Циклическая ссылка

node1 = null;
node2 = null;
// Оба объекта теперь недостижимы и будут собраны, несмотря на ссылки друг на друга.

Что можно и нельзя контролировать?

  • Нельзя напрямую управлять моментом зачистки или принудительно вызвать её в большинстве сред (кроме нестандартных методов).
  • Можно помочь GC, минимизируя удержание ссылок на крупные данные:
    *   Использовать **локальные переменные** вместо глобальных для временных данных.
    *   Явно удалять ссылки (`null`) на объекты, которые уже не нужны (особенно в долгоживущих контекстах, таких как Single Page Application).
    *   Осторожно использовать **closures** (замыкания), так как они могут неожиданно сохранять ссылки на большие объекты:

// Проблема: замыкание сохраняет ссылку на большой массив
function createHeavyClosure() {
    let largeArray = new Array(1000000).fill('data');
    return function() {
        // Эта функция, даже если не использует largeArray явно,
        // сохраняет ссылку на всю область видимости createHeavyClosure!
        console.log('closure');
    };
}
let closureFn = createHeavyClosure();
// largeArray остается в памяти, пока существует closureFn.

Практические рекомендации

  1. Профилирование памяти: Используйте инструменты разработчика (Chrome DevTools Memory tab) для отслеживания утечек памяти и понимания поведения GC.
  2. WeakRef и FinalizationRegistry (ES2021): Для особых случаев можно использовать WeakRef для слабых ссылок и FinalizationRegistry для получения уведомлений о очистке объектов (но с осторожностью).
// Пример WeakRef (не препятствует сборке мусора)
let obj = { data: 'large' };
let weakRef = new WeakRef(obj);

obj = null; // Основная ссылка уничтожена.
// Теперь объект может быть очищен GC. weakRef.deref() может вернуть undefined.

Вывод: Зачистка кучи — это автоматический, непрерывный и в основном неконтролируемый процесс, который происходит при нехватке памяти или в оптимальные моменты, определяемые движком JavaScript. Задача разработчика — писать код, который не создает непреднамеренных долгоживущих ссылок на данные, позволяя GC эффективно работать.