Утечки памяти и GC в Node
Перезапускать процесс — это не решение утечек. Нужно понимать, почему объекты не уходят: ссылки, замыкания, глобальные переменные, кэши без вытеснения, подписки на события.
Главные источники утечек
| Источник | Механика |
|---|---|
| Замыкания | Скоуп функции залочен, пока жива возвращённая ф-ция |
| Глобальные переменные | global.x = ... — никогда не GC |
| Подписки на события | Каждый on('event') без off плодит callback |
| Кэши без TTL/лимита | Map растёт бесконечно |
| Ссылки в long-lived объектах | arr.push(big) и не очистил |
| OS-дескрипторы | GC их не освобождает |
«Garbage Collection, который у нас есть в ноде, объекты операционной системы он не освобождает и не отпускает. Кроме того, мы сами должны следить за их освобождением.»
Структура памяти процесса Node
const { rss, heapTotal, heapUsed, external } = process.memoryUsage;
// rss — вся память процесса (включая C++ и сторонние буферы)
// heapTotal — выделено V8 под кучу
// heapUsed — занято объектами JS
// external — память, удерживаемая JS-объектами вне V8 heap (Buffer, ArrayBuffer)
«Память делится на: код C++, код JS, стек, куча, external. Куча (heap) чистится garbage collection.»
Как искать утечки
--inspectNode + Chrome DevTools → Memory tab.- Three-snapshot technique: снять heap snapshot → нагрузить → снапшот → нагрузить → снапшот. Сравнить «Comparison», смотреть на объекты, число которых растёт между всеми тремя.
--expose-gc+global.gcдля форсированного GC перед снапшотом.- Sampling heap profiler — выявить кто аллоцирует.
Кэш без вытеснения
// УТЕЧКА: Map растёт бесконечно
const cache = new Map();
function get(k) {
if (!cache.has(k)) cache.set(k, compute(k));
return cache.get(k);
}
// Решение: LRU или WeakMap
import { LRUCache } from 'lru-cache';
const cache = new LRUCache({ max: 500 });
Подводные камни
setIntervalбезclearIntervalдержит callback и весь его scope.- DOM-листенеры в Node-окружениях (jsdom) — то же.
WeakMap/WeakSet/WeakRefне держат ключ — но и не итерируются.- Memory growth ≠ leak. JS-приложение может расти и потом резко обвалить heap при major GC.
Источники
- Утечки памяти в Node.js и JavaScript, сборка мусора и профилирование · 2018-12-19
- Цитата: «Утечки порождают: ссылки в объектах, замыкания, глобальные переменные.»
- Цитата: «Кэш сам по себе не утечка, но требует контроля размера.»
- Разбираем видео Утечки памяти в SSR. Владимир Захаров (razbor10) · AsForJS · 2023-09-09