Garbage Collection в V8

V8 GC — generational: молодые объекты (Scavenger) и старые (Mark-Sweep / Mark-Compact). Цель — короткие паузы и максимальная throughput. Понимать важно потому, что GC-паузы напрямую бьют по FPS и latency.

Поколения

Зона GC Когда
New space (Young generation, ~1-8 MB) Scavenger (semi-space, копирующий) Часто, дёшево
Old space (Old generation) Mark-Sweep / Mark-Compact Реже, дороже
Large object space Mark-Sweep Для объектов > ~500 KB

Scavenger (молодые)

  • Делит New space на from-space и to-space.
  • Живые объекты копируются from→to. Мёртвые просто перезаписываются.
  • Объект, переживший 2 цикла, повышается в Old space.

Mark-Sweep / Mark-Compact (старые)

  • Mark — обход графа объектов от корней (stack, globals).
  • Sweep — освобождение неотмеченных.
  • Compact — дефрагментация (опционально, дорого).
  • Делается инкрементально и конкурентно, чтобы не блокировать main thread.

Концепции

  • Stop-the-world — пауза main thread на GC. V8 минимизирует через incremental marking.
  • Write barriers — V8 отслеживает, когда старый объект указывает на новый — иначе Scavenger пропустит.
  • Idle GC — V8 может «дочистить» во время простоя (rAF idle callback).

Что бьёт по GC

  • Аллокация мусора в горячем цикле (for (...) { const tmp = {...} }).
  • Большие объекты — сразу в Old space.
  • Закрытия, удерживающие большие скоупы.
  • Tonnaе короткоживущих promise-ей.

Подводные камни

  • GC не освобождает OS-ресурсы — файловые дескрипторы, сокеты. Закрывай руками.
  • --expose-gc + global.gc помогают только в тестах. В проде это контрпродуктивно.
  • WeakRef позволяет смотреть на объект без удержания, но нельзя полагаться на момент очистки.

Источники

См. также