Skip to main content
Практика завдань

Як працює збір сміття в Node.js (V8)?

Збирання сміття в Node.js

Node.js використовує движок JavaScript V8, який застосовує автоматичний збірник сміття (GC) для управління пам'яттю. Розуміння його роботи є важливим для написання ефективних з точки зору пам'яті додатків.


Структура пам'яті V8

V8 ділить купу на дві основні області:

ОбластьПризначенняСтратегія GC
Молода генерація (New Space)Короткоживучі об'єктиScavenge (незначний GC)
Стара генерація (Old Space)Довгоживучі об'єктиMark-Sweep-Compact (значний GC)
┌─────────────────────────────────────────┐ │ V8 Heap │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ Young Gen │ │ Old Gen │ │ │ │ (1-8 MB) │ │ (до ~1.5 GB) │ │ │ │ ┌────┬────┐ │ │ │ │ │ │ │From│ To │ │ │ Mark-Sweep- │ │ │ │ │ │ │ │ │ Compact │ │ │ │ └────┴────┘ │ │ │ │ │ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────────┘

Scavenge (незначний GC) — Молода генерація

Працює за алгоритмом Чені (копіювання напівпростору):

  1. Нові об'єкти виділяються в "From" простір
  2. Коли "From" заповнюється, живі об'єкти копіюються в "To" простір
  3. "From" простір очищається повністю
  4. Простори обмінюються ролями
js
// Об'єкти починаються в Молодій генерації let temp = { data: 'короткоживучий' }; // → Молод. Ген. (From простір) temp = null; // підлягає GC на наступному зборі
  • Дуже швидко (копіює лише живі об'єкти, а не всі)
  • Об'єкти, які пережили 2 збори, підвищуються до Старої генерації

Mark-Sweep-Compact (значний GC) — Стара генерація

Трифазний процес для довгоживучих об'єктів:

1. Фаза позначення

Починаючи з коренів GC (глобальні, стек), збирач проходить граф об'єктів, позначаючи всі досяжні об'єкти.

2. Фаза очищення

Всі непозначені об'єкти звільняються.

3. Фаза компактації (додаткова)

Пам'ять дефрагментується шляхом переміщення живих об'єктів разом.

Позначення: [●][○][●][○][○][●] (● = живий, ○ = мертвий) Очищення: [●][ ][●][ ][ ][●] Компактація: [●][●][●][ ]

Інкрементний та паралельний GC

V8 не зупиняє світ для всього GC. Він використовує:

ТехнікаОпис
Інкрементне позначенняПозначення виконується маленькими кроками, чергуючи з виконанням JS
Паралельне очищенняОчищення відбувається на фонових потоках
Паралельна компактаціяЧасткова компактація на фонових потоках
Ліниве очищенняСторінки очищуються лише тоді, коли виділення потребує цього

Моніторинг GC

bash
# Запустіть з трасуванням GC node --trace-gc server.js # Приклад виходу: # [12345:0x1] 52 ms: Scavenge 4.2 (6.3) -> 3.8 (8.3) MB, 1.2 / 0.0 ms # [12345:0x1] 102 ms: Mark-sweep 8.1 (12.3) -> 6.2 (12.3) MB, 5.1 / 0.0 ms
js
// Відкрити GC для ручного виклику (тільки для тестування!) // node --expose-gc script.js if (global.gc) { global.gc(); // Примусове збирання сміття } // Використання хуків продуктивності const { PerformanceObserver } = require('perf_hooks'); const obs = new PerformanceObserver((list) => { list.getEntries().forEach((entry) => { console.log(`GC: ${entry.kind} - ${entry.duration.toFixed(2)}ms`); }); }); obs.observe({ entryTypes: ['gc'] });

Обмеження пам'яті V8

ПлатформаЗа замовчуванням обмеження купи
64-біт~1.5 GB
32-біт~512 MB

Перезаписати з:

bash
node --max-old-space-size=4096 server.js # 4GB node --max-semi-space-size=64 server.js # 64MB молода ген

Найкращі практики

ПрактикаЧому
Уникайте великих довгоживучих об'єктівЗменшує тиск на Стару Генерацію
Використовуйте WeakMap / WeakRef для кешівДозволяє GC збирати записи
Уникайте замикань, які захоплюють великі областіЗапобігає ненавмисному утриманню
Передавайте великі дані замість буферизаціїТримає Молоду Генерацію маленькою
Моніторте з --trace-gc на стадіїВиявляйте паузи GC на ранніх етапах

Ключове усвідомлення: Більшість проблем з продуктивністю в Node.js викликані надмірними паузами GC у Старій Генерації. Тримайте свою Стару Генерацію стрункою, уникаючи необмежених кешів, замикань над великими об'єктами та завжди очищаючи посилання.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?
Практика завдань