Утечки памяти в 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
Вам нужно проверить что адрес вашего приложения прописан в списке серверов, которые проверяет хром.
после того как вы добавите