Понимание Сборки Мусора И Обнаружение Утечек Памяти В Node.js

Плохая пресса о Node.js часто ссылается на проблемы с производительностью.

Это не означает, что у Node.js больше проблем, чем у других технологий.

Пользователю просто необходимо иметь в виду некоторые особенности его эксплуатации.

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

Их необходимо понимать, чтобы предотвратить возникновение ошибок в работе.

И если что-то пойдет не так, нужно знать, как быстро все привести в порядок.

В этой статье Дэниел Хан рассказывает о том, как Node.js управляет памятью и как отслеживать проблемы, связанные с памятью.



Понимание сборки мусора и обнаружение утечек памяти в Node.js

В отличие от таких платформ, как PHP, приложения Node.js представляют собой долгосрочные процессы.

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

Но эта же особенность может создать проблемы.

Во-первых, давайте взглянем на основы Node.js.

Понимание сборки мусора и обнаружение утечек памяти в Node.js

Настоящий австрийский сборщик мусора Node.js — это программа C++, управляемая движком JavaScript V8. Гугл В8 — движок, изначально написанный для Google Chrome, но его можно использовать и автономно.

Поэтому он идеально подходит для Node.js и является, по сути, единственной частью платформы, «понимающей» JavaScript. V8 компилирует JavaScript в машинный код и выполняет его.

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

Это означает, что когда речь идет об управлении памятью в Node.js, мы фактически говорим о V8. Здесь Вы можете увидеть простой пример использования V8 с точки зрения C++.

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

Это место называется Resident Set. V8 использует дизайн, аналогичный виртуальной машине Java, и делит память на сегменты: Код: выполняющийся в данный момент код. Куча: содержит все примитивные типы значений (например, целые или логические) с указателями, которые ссылаются на объекты в куче и определяют поток управления программой.

Куча: Сегмент памяти, предназначенный для хранения ссылочных типов, таких как объекты, строки и замыкания.



Понимание сборки мусора и обнаружение утечек памяти в Node.js

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

memoryUsage().

Функция вернет объект, содержащий:

  • Размер резидентного набора;
  • общий размер кучи;
  • объем пространства, используемого в куче.

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



Понимание сборки мусора и обнаружение утечек памяти в Node.js

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

Процесс выделения и освобождения памяти называется вывоз мусора.

Введение в сбор мусора Каждой программе, потребляющей память, необходим механизм резервирования и освобождения места.

В C и C++ эта функция выполняется командами malloc() и free(), как показано в примере ниже:

  
   

char * buffer; buffer = (char*) malloc (42); // Do something with buffer free (buffer);

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

Мы называем его утечка памяти.

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

Это означает, что мы не можем активно выделять и освобождать память в JavaScript. Для решения этой проблемы V8 использует хорошо известный механизм под названием «сборка мусора».

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

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



Понимание сборки мусора и обнаружение утечек памяти в Node.js

Граф кучи.

Красный объект можно удалить только в том случае, если на него больше нет ссылок.

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

Чтобы исправить эту ситуацию, V8 использует 2 типа сборки мусора:

  • Сбор мусора – быстрый, но неполный;
  • Mark-Sweep работает относительно медленно, но очищает все неиспользуемые ссылки.

Отличный пост, содержащий очень подробную информацию о сборе мусора, можно найти по этой ссылке.

Теперь, взглянув на график, созданный методомprocess.memoryUsage(), вы можете легко различить разные типы сборки мусора: узор, напоминающий зубья пилы, отмечает работу Scavenge, отметки спуска представляют собой Mark-Sweep. Использование встроенного модуля узел-GC-профилер , вы можете получить еще больше информации о том, как работает сборщик мусора.

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

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

Понимание сборки мусора и обнаружение утечек памяти в Node.js

Продолжительность и частота запусков сборщика мусора Хорошо видно, что Scavenge запускается гораздо чаще, чем Mark-Sweep. В зависимости от сложности заявки продолжительность может варьироваться.

Примечательно, что на этом графике можно увидеть частые и кратковременные запуски Mark-Sweep, функция которых мне пока не ясна.

Когда дела идут плохо Если сборщик мусора сам очищает память, чего нам волноваться? Фактически, в ваших журналах могут легко возникнуть утечки памяти.



Понимание сборки мусора и обнаружение утечек памяти в Node.js

Исключение утечки памяти Используя график, который мы создали ранее, мы видим, как память постоянно засоряется!

Понимание сборки мусора и обнаружение утечек памяти в Node.js

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

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

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

Создание утечки памяти Некоторые утечки очевидны — например, хранение данных в глобальных переменных (например, добавление в массив IP-адресов всех вошедших в систему пользователей).

Другие не так заметны — например, знаменитый Утечка памяти Walmart из-за отсутствия маленькое выражение в основном коде Node.js, на поиск исходного кода которого ушли недели.

Я не буду здесь рассматривать ошибки кода ядра.

Давайте просто посмотрим на труднообнаружимую утечку кода.

из блога Метеор, который вы можете легко разрешить в своем коде.



Понимание сборки мусора и обнаружение утечек памяти в Node.js

Внедрение ошибки в ваш код JavaScript На первый взгляд выглядит нормально.

Вы можете подумать, что theThing перезаписывается каждый раз, когда вызывается replaceThing().

Проблема в том, что someMethod имеет свою собственную область видимости в качестве контекста.

Это означает, что someMethod() знает о unused() и, даже если unused() никогда не вызывается, этот факт не позволит сборщику мусора освободить память от originalThing. Просто потому, что косвенных вызовов слишком много.

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

Разве не было бы здорово, если бы вы могли заглянуть в кучу и посмотреть, что там сейчас? К счастью, такая возможность есть! V8 позволяет вам выгружать текущую кучу, а V8-profiler позволяет использовать эту функциональность для JavaScript.

/** * Simple userland heapdump generator using v8-profiler * Usage: require('[path_to]/HeapDump').

init('datadir') * * @module HeapDump * @type {exports} */ var fs = require('fs'); var profiler = require('v8-profiler'); var _datadir = null; var nextMBThreshold = 0; /** * Init and scheule heap dump runs * * @param datadir Folder to save the data to */ module.exports.init = function (datadir) { _datadir = datadir; setInterval(tickHeapDump, 500); }; /** * Schedule a heapdump by the end of next tick */ function tickHeapDump() { setImmediate(function () { heapDump(); }); } /** * Creates a heap dump if the currently memory threshold is exceeded */ function heapDump() { var memMB = process.memoryUsage().

rss / 1048576; console.log(memMB + '>' + nextMBThreshold); if (memMB > nextMBThreshold) { console.log('Current memory usage: %j', process.memoryUsage()); nextMBThreshold += 50; var snap = profiler.takeSnapshot('profile'); saveHeapSnapshot(snap, _datadir); } } /** * Saves a given snapshot * * @param snapshot Snapshot object * @param datadir Location to save to */ function saveHeapSnapshot(snapshot, datadir) { var buffer = ''; var stamp = Date.now(); snapshot.serialize( function iterator(data, length) { buffer += data; }, function complete() { var name = stamp + '.

heapsnapshot'; fs.writeFile(datadir + '/' + name , buffer, function () { console.log('Heap snapshot written to ' + name); }); } ); }

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

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

Если есть утечка памяти, у вас может оказаться много таких файлов.

Поэтому вам нужно внимательно следить за этим и добавить в этот модуль функцию оповещения.

Chrome предоставляет ту же функциональность дампа кучи, и вы можете использовать инструменты разработчика Chrome для анализа дампов профилировщика V8.

Понимание сборки мусора и обнаружение утечек памяти в Node.js

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

Таким образом, инструменты разработчика Chrome позволяют сравнивать разные файлы.

Сравнивая два дампа, мы получаем дельту значений, показывающую, какие структуры увеличиваются между двумя дампами:

Понимание сборки мусора и обнаружение утечек памяти в Node.js

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

На переменную, содержащую строку звездочек и называемую longStr, ссылается originalThing, на который ссылается некоторый метод, на который ссылается.

Я думаю, вы поняли идею.

Эта длинная строка вложенных ссылок и контекстов закрытия предотвращает очистку longStr. Хотя этот пример дает очевидные результаты, процесс всегда один и тот же:

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

  • Сравните несколько дампов, чтобы понять, какие значения растут.
Окончательно Как видите, процесс сборки мусора довольно сложен, и даже корректный код может вызвать утечки памяти.

Используя встроенные функции V8 вместе с инструментами разработчика Chrome, вы можете понять, что вызывает утечки памяти, и, если вы встроите эту функцию в свое приложение, у вас будет все необходимое для устранения проблемы, когда она возникнет. Остается один вопрос: как устранить утечку? Ответ прост: просто добавьте theThing = null; до конца функции, и вы сохранены.

Теги: #сборщик мусора #js #разработка #игры #программирование #перевод #разработка сайтов #JavaScript #программирование #node.js

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.