Потребление памяти node js

Обновлено: 06.07.2024

Я ищу кроссплатформенный способ надежного мониторинга максимального потребления памяти в процессе Node.js, независимо от того, есть утечки или нет.

Процессы в моем случае - это и реальные приложения, и синтетические тесты.

Я ожидал бы, что это будет работать как

Можно было как-то отследить потребление памяти с помощью node --trace_gc . , но это привело к выводу, который трудно читать (и, вероятно, трудно анализировать программно). Кроме того, GC не возникало, когда сценарий заканчивался слишком быстро, даже если использование оперативной памяти было значительным.

Из того, что я видел по этому вопросу, обычно для этого предлагается memwatch , например:

Но в моем случае он срабатывал только тогда, когда GC уже произошел или вообще не срабатывал, поэтому он был бесполезен для определения пиков потребления ОЗУ.

Я бы предпочел по возможности избегать инструментов с графическим интерфейсом, таких как node-inspector .

Может ли это максимальное потребление памяти быть надежно получено в виде числа из самого приложения или одного CLI, кросс-платформенного?

2 ответа

Вы можете использовать метод Node.js process.memoryUsage() , чтобы получить статистику использования памяти:

Метод process.memoryUsage () возвращает объект, описывающий использование памяти процессом Node.js в байтах.

Возвращает объект следующего формата:

Чтобы получить максимальное потребление памяти в процессе Node.js, можно использовать метод process.nextTick . Метод process.nextTick() добавляет обратный вызов в следующую очередь тиков . Как только текущий ход поворота цикла событий завершится, будут вызваны все обратные вызовы, которые в данный момент находятся в следующей очереди тиков.

Если вы попытаетесь сравнить процесс внутри себя, у вас будут искаженные значения использования памяти. (если вы хотите больше информации об этом, просто прокомментируйте)

Это небольшой ( кроссплатформенный ) инструмент, который я написал для проверки использования памяти другим процессом, он порождает независимый процесс и каждые 100 мс следит за использованием памяти, чтобы найти самый высокий пик, выводит каждый раз новый пик найден и останавливается, как только ребенок закончился.

Он использует pidusage , который представляет собой кроссплатформенный процесс (cpu% и) память использование PID

Разрешает настройку spawn (аргументы для передачи вместе с spawn) [может быть обновлено для использования в командной строке]

Он также будет работать с любым двоичным именем узла, поскольку он будет использовать то, которое использовалось для запуска этого инструмента.

Будет производить следующий вывод:

Этот инструмент не позволяет добавлять раздувание к коду процесса, который вы действительно хотите тестировать, поэтому таким образом вы не получите другие значения использования памяти, поскольку ваш код раздувания / сравнительного анализа также добавит использование памяти этому процессу.

Недавно в компании Reside Real Estate столкнулись с проблемами: в самые ответственные моменты начал падать Node.js-сервер. Подозрение пало на память. Сотрудники компании прибегли к временным мерам, что позволило избавить от неудобств пользователей, и занялись поисками источника проблем. В результате им удалось найти и устранить неполадки.



В этом материале они рассказывают о том, как искать и устранять ошибки, связанные с использованием памяти. А именно, речь пойдёт об утечках памяти, и о ситуациях, когда программы используют гораздо больше памяти, чем им на самом деле нужно. Этот рассказ поможет тем, кто столкнётся с чем-то похожим, сразу понять причину странного поведения сервера и быстро вернуть его в строй.

Типы проблем с памятью

▍ Утечка памяти

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

В низкоуровневых языках вроде C утечки памяти часто возникают в ситуации, когда память выделяют, например так: buffer = malloc(num_items*sizeof(double)); , но не освобождают после того, как память больше не нужна: free(buffer); .

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

▍ Чрезмерное использование памяти

В ситуации чрезмерного использования памяти программа занимает гораздо больше памяти, чем ей нужно для решения возложенной на неё задачи. Например, такое может возникнуть тогда, когда ссылки на большие объекты хранят дольше, чем нужно для правильной работы программы, что предотвращает уничтожение этих объектов сборщиком мусора. Подобное случается и тогда, когда в памяти держат большие объекты, которые попросту не нужны программе (это вызывает одну из двух основных проблем, которые мы рассмотрим ниже).

Выявление проблем с памятью


Признаки утечки памяти, кроме того, включают в себя уменьшение производительности программы с течением времени. Если сервер периодически выполняет один и тот же процесс, который изначально быстр, а перед отказом постепенно становится медленнее, это, весьма вероятно, говорит об утечке памяти.

Признаки чрезмерного использования памяти обычно выражаются в низкой производительности программ. Однако, чрезмерное использование памяти без утечки со временем не приводит к падению производительности.

Временное решение проблемы


Параметр $SIZE задаётся в мегабайтах и, теоретически, может быть любым числом, которое имеет смысл на конкретном компьютере. В нашем случае был использован параметр 8000, который, с учётом особенностей работы сервера, позволил выиграть достаточно времени на исследования. Кроме того, мы увеличили динамическую память. Мы пользуемся Heroku, там это делается просто.

Также мы воспользовались сервисом Twilio, настроили его так, чтобы нас оповещали каждый раз, когда на сервер приходит запрос, требующий особенно много памяти. Это позволило нам наблюдать за запросом и перезапускать сервер после его завершения. Такое решение неидеально, но для того, чтобы наши пользователи не сталкивались с отказами, мы были готовы на всё, даже на круглосуточные дежурства без выходных.

Отладка

Итак, благодаря настройкам Node и организации мониторинга сервера мы выиграли время, которое можно было потратить на то, чтобы дойти до первопричины неполадки. На первый взгляд может показаться, что «проблема с памятью сервера» — это нечто ужасное, а для избавления от этой «проблемы» потребуются фантастические инструменты и умения. Однако, на самом деле, всё не так уж и страшно. Есть вполне доступные инструменты для исследования приложений, существует множество материалов, в которых можно найти подсказки. Мы, для исследования памяти Node-сервера, будем пользоваться инструментами разработчика Chrome.

▍ Снепшот кучи

«Утечка памяти» — это проблема, которая выражается в постоянно растущем размере кучи. В результате куча оказывается слишком большой для продолжения нормальной работы сервера. Поэтому в самом начале исследования нужно сделать несколько снепшотов (снимков состояния) кучи, с некоторым интервалом, и погрузиться в исследование этих снепшотов с использованием инструментов разработчика Chrome для того, чтобы понять, почему куча так велика и почему она растёт. Обратите внимание на то, что следует делать несколько снепшотов, через некоторое время, в результате можно будет изучить объекты, которые будут переходить из одного снепшота в другой. Эти объекты, вполне возможно, являются виновниками утечки памяти. Существует множество способов создать снепшот кучи.

▍ Использование heapdump для создания снепшотов кучи

Мы, для создания снимков кучи, пользовались heapdump. Этот npm-пакет оказался весьма полезным. Его можно импортировать в код и обращаться к нему в тех местах программы, где нужно делать снепшоты. Например, мы делали снепшот каждый раз, когда сервер получал запрос, который мог вызвать процесс, интенсивно использующий память. Тут же мы формировали имя файла, содержащее текущее время. Таким образом мы могли воспроизводить проблему, отправляя на сервер всё новые и новые запросы. Вот как это выглядит в коде:

▍ Использование удалённого отладчика Chrome для создания снепшотов кучи

Если вы работаете с Node 6.3. или с более поздней его версией, для создания снепшотов кучи можно использовать удалённый отладчик Chrome. Для того, чтобы это сделать, сначала запустите Node командой такого вида: node --inspect server.j s. Затем перейдите по адресу chrome://inspect . Теперь вы сможете удалённо отлаживать процессы Node. Чтобы сэкономить время, можете установить этот плагин Chrome, который автоматически откроет вкладку отладчика при запуске Node с флагом --inspect . После этого просто делайте снепшоты тогда, когда сочтёте это необходимым.


Средства удалённой отладки Chrome и создание снепшотов кучи

Загрузка снепшотов и определение типа проблемы с памятью

Следующий шаг заключается в загрузке снепшотов на закладке Memory (память) инструментов разработчика Chrome. Если вы использовали для создания снепшотов кучи удалённый отладчик Chrome, то они уже будут загружены. Если вы использовали heapdump, то вам понадобится загрузить их самостоятельно. Обязательно загружайте их в правильном порядке, а именно — в том, в котором они были сделаны.

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

Наша первая проблема с памятью выглядела, на закладке Memory инструментов разработчика Chrome, так, как показано ниже. Несложно заметить, что куча постоянно растёт. Это говорит об утечке памяти.


Куча увеличивается со временем — очевидная утечка памяти

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


Куча со временем не растёт — это не утечка памяти

Размер кучи со временем не меняется. Всё дело в том, что при чрезмерном использовании памяти её размер превышает некие ожидаемые показатели не всегда, а лишь при выполнении определённых операций. При этом снепшоты делаются в какие-то моменты, которые никак не привязаны к ситуациям с чрезмерным использованием памяти. Если в момент создания снепшота не происходило выполнения неправильно написанной ресурсоёмкой функции, тогда куча не будет содержать никакой ценной информации о памяти, используемой этой функцией.

Для выявления подобных проблем мы рекомендуем два способа, которые помогли нам обнаружить виновников проблемы — функцию и переменную. Это — запись профиля выделения памяти и создание снепшотов на сервере, находящемся под серьёзной нагрузкой.

Если вы используете версию Node 6.3 или более позднюю, вы можете записать профиль выделения памяти через удалённый отладчик Chrome, запустив Node с уже упоминавшимся ключом --inspect . Это даст сведения о том, как отдельные функции используют память с течением времени.


Запись профиля выделения памяти

Ещё один вариант заключается в отправке множества одновременных запросов к вашему серверу и в создании множества снепшотов во время обработки этих запросов (предполагается, что сервер работает асинхронно, как результат, некоторые снепшоты могут оказаться гораздо больше других, что укажет на проблему). Мы бомбардировали сервер запросами и делали снепшоты. Некоторые из них оказались очень большими. Исследованием этих снепшотов можно заняться для выявления источника проблемы.

Анализ снепшотов

Теперь у нас есть данные, которые вполне могут помочь найти виновников проблем с памятью. В частности, рассмотрим анализ ситуации, в которой размеры последовательно сделанных снепшотов растут. Вот один из снепшотов, который загружен на вкладке Memory инструментов разработчика Chrome.


Исследование утечки памяти — все функции указывают на наш сервис электронной почты

Показатель Retained Size — это размер памяти, освобождённой после того, как объект удалён вместе со своими зависимыми объектами, которые недостижимы из корневого объекта.

Анализ можно начать с сортировки списка по убыванию по параметру Retained Size, после чего приступить к исследованию больших объектов. В нашем случае имена функций указали нам на ту часть кода, которая вызывала проблему.

Так как мы были уверены в том, что перед нами утечка памяти, мы знали, что исследование стоит начать с поиска переменных с неподходящей областью видимости. Мы открыли файл index.js почтовой службы и тут же обнаружили переменную уровня модуля в верхней части файла.


Мы со всем этим разобрались, внесли необходимые изменения, протестировали проект ещё несколько раз и исправили в итоге утечку памяти.

Вторую проблему отлаживать было сложнее, но тут сработал тот же подход. Ниже показан профиль выделения памяти, который мы записали с использованием инструментов разработчика Chrome и ключа Node --inspect .


Поиск виновников чрезмерного использования памяти

Так же как при анализе данных в ходе поиска утечки памяти, многие имена функций и объектов с первого взгляда узнать не удаётся, так как находятся они на более низком уровне, чем код, который пишут для Node.js. В подобной ситуации следует, встретив незнакомое имя, записать его.

Профиль выделения памяти привёл нас к одной из функций, recordFromSnapshot , она стала хорошей отправной точкой. Наше исследование снепшота кучи, которое не особенно отличалось от исследования, выполняемого при поиске утечки памяти, позволило обнаружить очень большой объект target . Это была переменная, объявленная внутри функции recordFromSnapshot . Эта переменная осталась от старой версии приложения, она была больше не нужна. Избавившись от неё, мы исправили ситуацию с чрезмерным использованием памяти и ускорили выполнение процесса, которое раньше занимало 40 секунд, до примерно 10 секунд. При этом процессу не требовалась дополнительная память.

Итоги

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

Самый быстрый способ — увеличить количество памяти в Node.js. Начиная с версии v8 вы можете устанавливать ограничение в мегабайтах с помощью флага --max-old-space-size :

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

Аналогичного эффекта можно добиться с помощью другого флага:

Изменение ограничения памяти для всей среды Node.js

Чтобы изменить лимит памяти для всей среды, нужно установить значение переменной NODE_OPTIONS в конфигурационном файле (его расширение .bashrc, bash_profile или .zshrc и т. п.).

«Heap out of memory» во время nmp install

Если во время установки пакетов с помощью npn или yarn у вас появляется эта ошибка, вы можете увеличить лимит памяти на время установки.

Что означает эта ошибка?

По умолчанию в Node.js установлен лимит памяти, который не позволяет программе занять слишком много памяти и уронить всю систему. Лимит отличается на разных версиях Node.js и архитектурах (32бита или 64бита).

Ограничения памяти на разных версиях Node.js

Эти значения не объявлены официально, но с помощью небольшой программы можно получить такие значения для 64 битной архитектуры.

Лимиты памяти на разных версиях Node.js

4GB памяти в куче будет достаточно для большинства случаев

Чтобы проверить лимит памяти вашей системы, создайте файл index.js и добавьте в него следующий код:

Как избежать недостатка памяти в Node.js

Вот три альтернативных решения, которые позволят уменьшить потребление памяти.

Обработка данных по частям

Иногда нужно обработать большой набор данных. Например, вы пишите программу, которая принимает данные из CSV файла, очищает их и добавляет в БД (это называется ETL: извлечение, трансформация, загрузка).

Cube Dev , Удалённо , От 6000 $

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

Подробнее о том, как сделать это в MongoDB в этом ответе на StackOverflow.

Избегайте утечек памяти

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

Её содержание сводится к тому, что большинство утечек, которые можно отследить, вызваны неудалёнными ссылками на объекты, которые больше не нужны. Это может случиться, когда вы забыли удалить interval, timer или чрезмерно используете глобальные переменные.

Профилирование

Профилирование помогает обнаружить утечки памяти. На фронтенде это можно сделать в Chrome в Инструментах разработчика во вкладке Memory.

В Node.js начиная с версии 6.3.0 также можно использовать Chrome для отладки использования памяти.

Во-первых, запустите приложение в режиме проверки:

Затем откройте страницу в Chrome, введите адрес chrome://inspect и нажмите на кнопку Open dedicated DevTools for Node.

После этого откроется окно, в котором вы сможете подключиться к вашему Node.js приложению.

Devtools Node.js

Перезапуск процессов

Допустим, ваша программа работает на компьютере с ограниченным объёмом памяти, например Raspberry Pi.

Мы будем использовать cluster и библиотеки node v8.

Cluster даёт возможность воспользоваться преимуществами многоядерных систем и запускать кластер из процессов Node.js.

V8 предоставляет API для конкретной версии V8, используемой в Node.js.

Давайте разделим программу на две сущности: master и worker.

Master будет перезапускать worker`ов в случае, если они перестанут работать из-за переполнения кучи. Worker`ы будут отвечать за основную логику (в нашем случае запускать тяжёлую функцию heavyHeapConsumer).

total_heap_size — размер кучи, который можно увеличить.

heap_size_limit — максимально возможный размер кучи.

В коде worker`а устанавливается total_heap_size равный 85% от heap_size_limit. Затем worker каждую секунду проверяет не превышен ли лимит. Если лимит превышен, то процесс worker убивает себя.

Лимит (85%) и интервал проверки (1 секунда) нужно выбирать для каждого конкретного случая. Здесь функция heavyHeapConsumer увеличивает кучу каждые 100мс. Если в вашем варианте увеличение будет происходить каждые 10мс, то следует уменьшить лимит и увеличить интервал проверки.

Найти и обезвредить: утечки памяти в Node.js

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

Это довольно распространенная проблема. Дело в том, что многие разработчики пренебрегают управлением памятью из-за неправильных представлений об ее автоматическом выделении и освобождении в современных высокоуровневых языках программирования, например JavaScript.

Своевременно же не решенный вопрос ее утечек может обернуться резким снижением производительности приложения вплоть до невозможности его нормального использования.

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

Начнем с того, что ключ к их пониманию лежит в понимании принципов управления памятью в Node.js. А это, в свою очередь, означает, что мы должны разобраться, как это управление осуществляется движком V8, используемым Node.js для JavaScript.

Вкратце напомню вам структуру памяти в V8.

Главным образом она делится на две области: стек (stack) и кучу (heap).

1.Стек — это область памяти, в которой хранятся статические данные, включающие фреймы методов/функций, примитивные значения и указатели на объекты. Он управляется ОС.

2.Куча — это самая большая область памяти, в которойV8хранит объекты или динамические данные.Здесь же происходит сборка мусора.

Цитируя Дип К Сасидхаран, разработчика и одного из авторов книги “Развитие полного стека с JHipster”, отметим, что

“V8 управляет памятью кучи с помощью сборки мусора. Проще говоря, он освобождает память, используемую висячими объектами, т.е. объектами, на которые нет прямых или косвенных (через ссылку в другом объекте) ссылок из стека, для освобождения пространства с целью создания нового объекта.

Сборщик мусора в V8 отвечает за восстановление неиспользуемой памяти для повторного ее применения в процессе работы движка. Сборка мусора происходит по поколениям (объекты в куче распределяются по группам в зависимости от времени жизни и удаляются на разных этапах). В V8 существуют два этапа и три разных алгоритма сборки мусора”.

Что такое утечки памяти?

Простыми словами, утечка памяти — это не что иное, как фрагмент памяти в куче, который больше не нужен приложению, но который не был возвращен оперативной системе сборщиком мусора.

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

В чем же причина утечек памяти в JS?

Автоматическое управление памятью, подразумевающее сборку мусора в V8, предназначено для предотвращения ее утечек. Например, циклические ссылки больше не вызывают беспокойства, но все-таки могут возникать из-за нежелательных ссылок в куче или по каким-либо другим причинам.

Рассмотрим несколько самых распространенных причин:

1.Глобальные переменные. Поскольку на них в JavaScript ссылается корневой узел (глобальный объект window или ключевое слово this ), то они никогда не подвергаются сборке мусора в течение всего жизненного цикла приложения и будут занимать память до тех пор, пока оно выполняется. И это относится ко всем объектам, на которые ссылаются глобальные переменные, а также к их потомкам. Наличие большого графа объектов со ссылками из корня может привести к утечке памяти.

2. Множественные ссылки. Ситуации, когда на один и тот же объект ссылаются несколько объектов, также могут вызвать утечку памяти при условии, что одна из ссылок становится висячей.

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

4. Таймеры и события. Использование setTimeout , setInterval , Observers и слушателей событий может вызвать утечки памяти в том случае, если ссылки на объекты кучи хранятся в их обратных вызовах без правильной обработки.

Лучшие практики для предотвращения утечек памяти

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

Сокращение использования глобальных переменных

Поскольку глобальные переменные не подвергаются сборке мусора, то лучше всего убедиться, что вы не злоупотребляете их использованием. Ниже речь пойдет о том, как это сделать.

Обходимся без случайных глобальных переменных

Когда вы присваиваете значение необъявленной переменной, JavaScript по умолчанию определяет ее как глобальную. Это может произойти по ошибке и привести к утечке памяти. Подобное может случиться в результате присвоения переменной к this .

Во избежание таких сюрпризов, всегда пишите код JavaScript в режиме strict и используйте нотацию 'use strict'; в начале файла JS.

В режиме strict выше приведенный пример приведет к ошибке. Однако при использовании ES модулей и компиляторов, таких как TypeScript или Babel, нет необходимости включать данный режим, так как он будет задействован по умолчанию. В последних версиях Node.js вы можете активировать режим strict глобально, сопроводив выполнение команды node флагом --use_strict .

И наконец, помните, что не следует привязывать глобальное this к функциям, использующим методы bind или call , так как это лишает использование режима strict всякого смысла.

Умеренное использование глобальной области видимости

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

  1. Минимизируйте использование глобальной области видимости. Вместо этого рассмотрите возможность применения локальной области внутри функций, так как они будут удалены в процессе сборки мусора для освобождения памяти. Если вам вынужденно приходится прибегать к использованию глобальной переменной, то задайте ей значение null , когда в ней уже не будет необходимости.
  2. Используйте глобальные переменные только для констант, кэша и переиспользуемых объектов-одиночек. Не стоит применять их в целях избежания передачи значений в коде. Для обмена данными между функциями и классами передавайте значения в качестве параметров или атрибутов объектов.
  3. Не храните крупные объекты в глобальной области видимости. Если же вам приходится это делать, то не забудьте определить их как null, когда они более не нужны. В отношении объектов кэша рекомендуется установить обработчик для периодической очистки, препятствующий их неограниченному росту.

Эффективное использование стека

Максимально возможное использование переменных стека способствует эффективной и производительной работе памяти, так как доступ к стеку происходит гораздо быстрее, чем к куче. Это также позволяет избежать случайных утечек памяти.

Конечно, использование исключительно статических данных непрактично. В реальных приложениях нам приходится работать со многими объектами и динамическими данными. Но мы можем оптимизировать применение переменных стека при помощи ряда приемов:

1.Избегайте использования ссылок переменных стека на объекты кучи по мере возможности и не храните неиспользуемые переменные;

2.Деструктуризируйте и используйте только необходимые поля объектов или массивов вместо того, чтобы целиком передавать их функциям, замыканиям, таймерам и обработчикам событий. Тогда ссылки на объекты уже не будут оставаться в замыканиях. Передаваемые поля могут быть в основном примитивами, которые будут храниться в стеке.

Эффективное использование кучи

В любом реальном приложении мы так или иначе будем использовать кучу, но с помощью следующих рекомендаций можно сделать работу с ней более эффективной:

1.По возможности копируйте объекты вместо того, чтобы передавать ссылки. Их передача возможна только в том случае, если объект крупный или операция копирования требует больших затрат.

2.По максимуму обходитесь без мутаций объекта. Вместо этого для их копирования используйте распространение объекта или Object.assign .

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

4.Используйте переменные с коротким жизненным циклом.

5.Старайтесь не создавать огромные деревья объектов. Если же это неизбежно, то обеспечьте им короткий жизненный цикл в локальной области видимости.

Грамотное использование замыканий, таймеров и обработчиков событий

Как уже было отмечено ранее, замыкания, таймеры и обработчики событий — это те области, где могут произойти утечки памяти. Начнем с замыканий, как наиболее часто встречающихся в коде JavaScript. Посмотрим на следующий код команды Meteor, который приводит к утечке памяти, так как переменная longStr не подлежит удалению и увеличивает объём занимаемой памяти.

Выше обозначенный код создает несколько замыканий, которые удерживают ссылки на объекты. В этом случае устранение утечки памяти возможно путем определения originalThing как null в конце выполнения функции replaceThing . Подобных ситуаций тоже можно избежать, создавая копии объектов и соблюдая выше описанный немутабельный подход.

Когда дело касается таймеров, всегда передавайте копии объектов и обходитесь без мутаций. По окончании работы таймеров проводите их очистку с помощью методов clearTimeout и clearInterval .

Тоже самое относится к слушателям событий и наблюдателям. Как только они выполнили свою задачу, очистите их. Не оставляйте слушателей в постоянно работающем состоянии, особенно если они удерживают ссылки на объекты из родительской области видимости.

Заключение

В настоящее время утечки памяти в JavaScript не являются такой уж большой проблемой, как бывало раньше, благодаря эволюции движков JS и улучшениям в языке. Но если мы не будем внимательны, то они по-прежнему будут происходить и приводить к снижению производительности и сбоям в работе приложения/ОС.

Во-первых, чтобы убедиться, что наш код не приводит к утечкам памяти в приложении Node.js, нужно разобраться в принципах ее управления движком V8. Во-вторых, важно понять, что послужило их причиной.

После этого в наших силах избежать развития таких ситуаций. Но если все-таки мы обнаружим утечку памяти/проблемы с производительностью, то теперь будем знать, что искать.

Читайте также: