Примеры замыканий в node.js, которые могут вызвать утечку памяти
При попытке отладки утечек памяти в NodeJS я нахожу это довольно сложным (учитывая отсутствие инструментов профилирования, о которых я знаю).
Мне показалось, что я вернусь к основам и убедился, что понимаю, как утечка памяти будет создана специально в NodeJS. Я путаюсь о типах замыканий, которые могут вызвать утечку памяти, и я не уверен, что нужно сборщику мусора, чтобы освободить эту память.
Не могли бы вы привести несколько примеров базовых шаблонов, которые могли бы вызвать утечку памяти в Node.js?
Ответы
Ответ 1
Не "утечка" точно, но это может быть общей ошибкой.
var fn = (function() {
var a = "super long string ...";
var b = "useless value";
var c = "Hello, World!";
return function() {
return c;
};
})();
Это возвращает функцию, которая ссылается на область действия, и каждая локальная переменная в этой области будет сохранена, хотя требуется только одно из этих значений. Это приводит к большему количеству использования памяти, чем вам нужно, особенно если ваша функция использует небольшую переменную, но в этой области есть большие значения, которые вам не нужно поддерживать.
Как это исправить?
Простая опция - обнулить переменные, которые вам не нужны в конце вашей функции. Переменные все еще находятся в области видимости, но их данные будут опубликованы.
var fn = (function() {
var a = "super long string ...";
var b = "useless value";
var c = "Hello, World!";
// do stuff with a and b
a = b = null;
return function() {
return c;
};
})();
Или вы можете сломать все, что использует temp vars, в свою собственную функцию, чтобы их возможности могли быть освобождены. Это лучшее решение для более крупного проекта.
var doSetup = function() {
var a = "super long string ...";
var b = "useless value";
// do stuff with a and b
};
var fn = (function() {
doSetup();
var c = "Hello, World!";
return function() {
return c;
};
})();
Ответ 2
-
Вы можете использовать node-inspector для отладки node приложений с помощью инструментов Chrome dev.
-
Принятый ответ о неиспользуемых переменных замыкания неверен, потому что в современных JS-машинах включены только те переменные, которые фактически ссылаются во внутренней функции. Остальные автоматически собирают мусор. Другими словами, это теоретическое условие никогда не произойдет в Node.
-
Для реального (и довольно распространенного) примера с использованием express вы можете создать промежуточное программное обеспечение, которое загружает файл в память для каждого запроса, а затем бросает необработанное исключение в том же запросе, улавливает заброшенное исключение и затем терпит неудачу для выхода из процесса.
Исключенное исключение заставит загруженные ресурсы запроса задерживаться, а не очищаться в конце цикла запроса/ответа.
Невозможность выйти из процесса, когда возникает исключение, означает, что вместо отключения и перезапуска чего-то вроде PM2 или Forever, node будет игнорировать ошибку и продолжать обслуживать новые запросы, как будто ничего не произошло. Поскольку ресурсы не очищаются, процесс будет потреблять все больше и больше памяти с течением времени, пока он не перетащит производительность машины и, в конечном итоге, не достигнет места для выделения новых ресурсов.
Это, очевидно, отрицательно скажется на опыте пользователя.
См. также Почему утечка ресурса причины исключения в Node.js.
Ответ 3
От: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Closures#Closures
Однако следует иметь в виду, что закрытие сохраняет указатель на его охватывающий объем. В результате присоединение замыкания к элементу DOM может создать круговую ссылку и, следовательно, утечку памяти. Например, в следующем коде:
function foo(element, a, b) {
element.onclick = function() { /* uses a and b */ };
}
закрытие функции сохраняет ссылку на элемент, a и b, даже если он никогда не использует элемент. Поскольку элемент также содержит ссылку на закрытие, у нас есть цикл, который не будет очищен сборкой мусора. В этих ситуациях код может быть структурирован следующим образом:
function foo(element, a, b) {
element.onclick = bar(a, b);
}
function bar(a, b) {
return function() { /* uses a and b */ }
}