Примеры замыканий в 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 */ }
}