сборка мусора с помощью node.js - PullRequest
29 голосов
/ 16 марта 2011

Мне было любопытно, как шаблон вложенных функций node.js работает с сборщиком мусора v8. вот простой пример

readfile("blah", function(str) {
   var val = getvaluefromstr(str);
   function restofprogram(val2) { ... } (val)
})

если restofprogram долго работает, не значит ли это, что str никогда не будет собирать мусор? Насколько я понимаю, с узлом у вас много вложенных функций. Получает ли это мусор, если restofprogram был объявлен снаружи, поэтому str не может быть в области видимости? Это рекомендуемая практика?

РЕДАКТИРОВАТЬ Я не собирался усложнять проблему. Это была просто небрежность, поэтому я изменил ее.

Ответы [ 3 ]

66 голосов
/ 16 марта 2011

Простой ответ: если значение str не указано нигде (а само str не указано из restofprogram), оно станет недоступным, как только вернется function (str) { ... }.

Подробности: компилятор V8 различает действительные локальные переменные от так называемых context переменных, захваченных замыканием, затемняемых с -столкновением или eval вызовом,

Локальные переменные живут в стеке и исчезают, как только завершается выполнение функции.

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

function outer () {
  var x; // real local variable
  var y; // context variable, referenced by inner1
  var z; // context variable, referenced by inner2

  function inner1 () {
    // references context 
    use(y);
  }

  function inner2 () {
    // references context 
    use(z);
  }

  function inner3 () { /* I am empty but I still capture context implicitly */ } 

  return [inner1, inner2, inner3];
}

В этом примере переменная x исчезнет, ​​как только вернется outer, а переменные y и z исчезнут только тогда, когда оба inner1, inner2 и inner3 умрут.Это происходит потому, что y и z размещены в одной и той же контекстной структуре, и все три замыкания неявно ссылаются на эту контекстную структуру (даже inner3, которая не использует ее явно).

Ситуация становится еще более сложнойкогда вы начинаете использовать с -статистом, try / catch -статем, который в V8 содержит неявное с -статином внутри предложения catch или global eval.

function complication () {
  var x; // context variable

  function inner () { /* I am empty but I still capture context implicitly */ }

  try { } catch (e) { /* contains implicit with-statement */ }

  return inner;
}

В этом примере x исчезнет, ​​только когда умрет inner.Потому что:

  • try / catch - содержит неявное с - утверждение в предложении catch
  • V8 предполагает, что любое с тени состояния все местные жители

Это заставляет x стать контекстной переменной, а inner захватывает контекст, так что x существует до тех пор, пока inner не умрет.

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

4 голосов
/ 16 марта 2011

На самом деле ваш пример несколько хитрый.Это было нарочно?Вы, похоже, маскируете внешнюю переменную val с внутренним лексически ограниченным аргументом restofprogram () val вместо фактического ее использования.Но в любом случае, вы спрашиваете о str, поэтому позвольте мне проигнорировать хитрость val в вашем примере просто для простоты.

Я думаю, что переменная str не будетполучить до того, как завершится функция restofprogram (), даже если она не использует ее. Если restofprogram () не использует str и , то не использует eval() и new Function(), тогда его можно безопасно собрать, ноЯ сомневаюсь, что это будет.Это была бы сложная оптимизация для V8, вероятно, не стоит проблем.Если бы в языке не было eval и new Function(), то это было бы намного проще.

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

Теперь мне интересно, не имели ли вы в виду что-то еще, кроме того, что вы написали в своем примере.Вся программа в Node такая же, как в браузере - она ​​просто регистрирует обратные вызовы событий, которые запускаются асинхронно позже после того, как основное тело программы уже завершено.Кроме того, ни один из обработчиков не блокируется, так что ни одна функция не требует заметного времени для завершения.Я не уверен, что понял, что вы на самом деле имели в виду в своем вопросе, но надеюсь, что написанное мной поможет понять, как все это работает.

Обновление:

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

Если ваша программа выглядит примерно так:

readfile("blah", function (str) {
  var val = getvaluefromstr(str);
  // do something with val
  Server.start(function (request) {
    // do something
  });
});

Тогда вы также можете написать это так:

readfile("blah", function (str) {
  var val = getvaluefromstr(str);
  // do something with val
  Server.start(serverCallback);
});
function serverCallback(request) {
  // do something
});

После того, как Server.start () будет вызван, str выйдет из области видимости и в конечном итоге будет собран.Кроме того, это сделает ваши отступы более управляемыми, что нельзя недооценивать для более сложных программ.

Что касается val, вы можете сделать его глобальной переменной в этом случае, что значительно упростит ваш код.Конечно, вам не нужно, вы можете бороться с замыканиями, но в этом случае создание val глобального или включение его во внешнюю область, общую как для обратного вызова readfile, так и для функции serverCallback, кажется наиболее простым решением.

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

1 голос
/ 16 марта 2011

Я предполагаю, что str НЕ будет собирать мусор, потому что он может быть использован restofprogram (). Да, и str должен получить GCed, если restofprogram был объявлен снаружи, за исключением случаев, когда вы делаете что-то вроде этого:

function restofprogram(val) { ... }

readfile("blah", function(str) {
  var val = getvaluefromstr(str);
  restofprogram(val, str);
});

Или если getvaluefromstr объявлен примерно так:

function getvaluefromstr(str) {
  return {
    orig: str, 
    some_funky_stuff: 23
  };
}

Последующий вопрос: v8 делает просто GC или комбинацию GC и ref. считая (как питон?)

...