Как выявлять и исправлять утечки памяти
Что проверяют
Есть ли у вас рабочий порядок действий: как отличить утечку от обычного роста кучи, как снять и сравнить снимки, где искать виновника. Следом часто просят случай из практики и спрашивают, как снять дамп на проде и не уронить сервис.
Как отвечать
Сначала подтвердите, что это утечка. По process.memoryUsage().heapUsed или метрикам APM память после сборок мусора ходит пилой, а утечка это когда нижние точки пилы часами ползут вверх. Растущий rss при ровном heapUsed указывает на память вне кучи: Buffer, нативные модули.
Дальше снимки кучи:
- локально:
node --inspect, Chrome DevTools, вкладка Memory. Три снимка: после прогрева, после серии запросов, после ещё одной. В режиме Comparison видно, какие объекты растут, а в панели Retainers, кто их держит; - на стенде или проде: встроенный
v8.writeHeapSnapshot()или флаг--heapsnapshot-signal=SIGUSR2, пакетheapdumpуже не нужен.--heapsnapshot-near-heap-limitснимет дамп перед падением от нехватки памяти.
Типичные виновники в Node.js:
- неограниченный кэш:
Mapна уровне модуля, куда пишут на каждый запрос. Лечится лимитом и вытеснением черезlru-cacheилиWeakMap, если ключ объект; - подписки:
emitter.onвнутри обработчика запроса безoff, первым симптомом часто бываетMaxListenersExceededWarning; - таймеры и замыкания:
setIntervalбезclearInterval, замыкание, которое держит весьreqили большой буфер; - очереди и промисы: очередь без ограничения или промисы, которые никогда не завершаются.
После правки повторите нагрузку и убедитесь, что базовая линия кучи стоит на месте.
Где спотыкаются
Советуют поднять --max-old-space-size. Это откладывает падение, а не лечит утечку.
Называют memwatch-next и heapdump. Первый заброшен и на современных версиях Node не собирается, второй заменён встроенным API.
Снимают дамп на проде, не думая о цене: запись снимка синхронная, блокирует event loop и требует памяти порядка размера кучи. Инстанс сначала выводят из балансировки.