Утечки памяти в Nodejs

Поздравляю. Вы открыли перевод статьи с медиума. Похлопайте автору оригинала 👏👏👏

Источник пикчи

Вам когда-нибудь приходилось разрабатывать большие Nodejs приложения? Если да, то ты знаешь о чем эта статья, если нет, то тебе в любом случае стоит продолжить читать потому что ты в любом случае с этим столкнешься как и я 😄. Утечки памяти очень часто встречаются в такого рода приложениях и многие большие компании уже успели пострадать от этого. Давайте разберемся почему так происходит.

Приложения Nodejs представляют собой длительный процесс — запускается один раз и все то время пока обрабатывает входящие запросы, непрерывно потребляет ресурсы. В отличии от PHP в который создает дочерний процесс на каждый входящий запрос. Для каждого создает приложение и высвобождает ресурсы только после того как запрос обработан.

Управление памятью в Nodejs

Все управление памятью работает через V8 — это движок, который позволяет запускать JavaScript вне бразуера.

V8 работает с двумя категориями памяти:

В стеке хранятся все примитивные типы данных: Number, String, Boolean, Null, Undefined, Symbol и ссылки на не примитивные типы Object.

В куче, соответственно, хранятся не примитивные типы Object.

У движка V8 есть сборщик мусора который работает по алгоритму Mark and Sweep (визуалиция на хабре, вопрос на SO, wiki). Он проверяет все ссылки на объекты от корня, помечает их и удаляет лишние.


Источник пикчи


Самые частые проблемы:

Учитывая все то что мы описали выше, вы можем определить три самых частых причины утечек памяти.

Глобальные переменные

Тут все просто: они имеют ссылку на корень и будут хранится приложением все время. Стоит быть более осмотрительным при их объявлении и обращать внимание на их размер.

Множественные ссылки

Установка множественных ссылок на один и тот же объект тоже может быть проблемой по банальной причине. Вы можете удалить ссылку в одном месте и забыть про неё в другом. GC найдет ссылку и оставит объект храниться в куче.

Замыкания

Замыкания это простой способ хранить ссылки на объекты что бы использовать их позже. У этой фишки есть много преимуществ, но она может вызвать проблемы. Ссылки на такие объекты будут лежать в куче и они могут быть действительно большими.

Как найти утечки

Существует много инструментов и библиотек предназначенных для нахождения утечек в Nodejs, все они работают по схожему принципу определения проблемы сравнивая две разных копии кучи и проверяя результаты. Ещё они дополнительно запускают сборщик мусора перед взятием копии памяти что бы убедиться что утечка действительно есть.

Разница между двумя дампами кучи показывает размер утекшей памяти в приложении. Я расскажу про два инструмента, которые на мой взгляд самые важные:

  • Node-MemwatchМожет быть очень полезно когда ваше приложение уже в используется на стадии продакшена. Это библиотека которая умеет оповещать об утечках соответствующим событием и предоставляет информацию об использовании памяти. Вы можете как-то обрабатывать эти события или просто ждать надвигающихся проблем с памятью😄.
  • Node-InspectorОсобо полезно в связке с Chrome DevTools во время разработки. Оно проводит стресс-тесты на вашем приложении и отслеживает использование памяти для проверки потенциальных утечек в коде. Оно так же может найти где же именно произошла утечка.

Быстрый пример работы со связкой Node-Inspector + Chrome DevTools

  • Во-первых, запустите свое приложение с флагами.

--expose-gc для того что бы явно запустить сборщик мусора

inspect=9222 что бы разрешить Chrome Debugger подключиться к вашему приложению на порту 9222.

Ваша команда должна выглядеть примерно как-то так:

 node --expose-gc --inspect=9222 app.js

Теперь вам нужно открыть chrome://inspect в Chrome

Вам нужно проверить что адрес вашего приложения прописан в списке серверов, которые проверяет хром.

после того как вы добавите