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позволяет смотреть на объект без удержания, но нельзя полагаться на момент очистки.
Источники
- Утечки памяти, GC и профилирование · 2018-12-19
- Реализация хранения данных. Стек и куча · AsForJS · 2024-07-27
- Тезис: в современном V8 не одна куча, а несколько сегментов