garbage collection with node.js

Byłem ciekaw, jak węzeł.wzorzec js zagnieżdżonych funkcji współpracuje z garbage collector w wersji v8. Oto prosty przykład

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

Jeśli restofprogram działa długo, czy to nie znaczy, że str nigdy nie będzie zbierać śmieci? Rozumiem, że w node często masz zagnieżdżone funkcje. Czy to dostaje śmieci zbierane jeśli restofprogram został zadeklarowany na zewnątrz, więc str nie może być w zakresie. Czy jest to zalecana praktyka?

EDIT {[7] } nie zamierzałem żeby skomplikować problem. To była nieostrożność, więc ją zmodyfikowałem.

Author: Vishnu, 2011-03-16

3 answers

Prosta odpowiedź: jeśli wartość str nie jest odwołana z nigdzie indziej (a sama str nie jest odwołana z restofprogram), stanie się nieosiągalna, gdy tylko function (str) { ... } powróci.

Details: kompilator V8 odróżnia rzeczywiste lokalne zmienne od tak zwanych kontekstu zmiennych przechwyconych przez zamknięcie, zacienionych przez z-instrukcją lub wywołaniem eval.

Zmienne lokalne żyją na stosie i znikają zaraz po wykonaniu funkcji / align = "left" /

Zmienne kontekstowe żyją w strukturze kontekstu przydzielonego stercie. Znikają, gdy struktura kontekstu umiera. Ważne jest, aby pamiętać, że zmienne kontekstowe z tego samego zakresu żyją w strukturze tej samej. Zilustruję to przykładowym kodem:

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];
}

W tym przykładzie zmienna x zniknie, gdy tylko outer zwróci, ale zmienne y i z znikną tylko wtedy, gdy obie inner1, inner2 oraz inner3 giń. Dzieje się tak, ponieważ y i z są przydzielone w tej samej strukturze kontekstu, a wszystkie trzy zamknięcia pośrednio odwołują się do tej struktury kontekstu (nawet inner3, która nie używa jej jawnie).

Sytuacja staje się jeszcze bardziej skomplikowana, gdy zaczniesz używać z-statement, try/catch-statement, które w wersji V8 zawiera implicit z-statement wewnątrz klauzuli catch lub 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;
}

W tym przykładzie x zniknie tylko wtedy, gdy inner umiera. Ponieważ:

  • try/catch -contains implicit with -statement in catch clause
  • V8 zakłada, że dowolne Z-twierdzenie cieni wszystkie lokalne

Wymusza to x stanie się zmienną kontekstową i inner przechwytuje kontekst tak, aby x istniał aż inner umrze.

Ogólnie jeśli chcesz mieć pewność, że dana zmienna nie zachowa jakiegoś obiektu dłużej niż jest to naprawdę potrzebne możesz łatwo zniszcz ten link przypisując {[24] } do tej zmiennej.

 63
Author: Vyacheslav Egorov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-17 18:11:16

Właściwie twój przykład jest nieco trudny. To było celowo? Wydajesz się być maskującym zewnętrzną zmienną val z wewnętrznym leksykalnie skalowanym argumentem restofprogram (), zamiast jej używać. Ale w każdym razie, pytasz o str, więc pozwól mi zignorować podstępność val w twoim przykładzie, tylko dla uproszczenia.

Domyślam się, że zmienna str nie zostanie zebrana przed zakończeniem funkcji restofprogram (), nawet jeśli nie użyje to. Jeśli restofprogram() nie używa str i nie używa eval() i new Function() wtedy to może być bezpiecznie zebrane, ale wątpię, żeby to było. Byłaby to trudna Optymalizacja dla V8, prawdopodobnie nie warta zachodu. Gdyby nie było eval i new Function() w języku, byłoby o wiele łatwiej.

Nie musi to oznaczać, że nigdy nie zostanie pobrane, ponieważ każda obsługa zdarzeń w jednowątkowej pętli zdarzeń powinna zakończyć się niemal natychmiast. W przeciwnym razie cały proces zostałby zablokowany i miałbyś większe problemy niż jedna bezużyteczna zmienna w pamięci.

Teraz zastanawiam się, czy nie miałeś na myśli czegoś innego niż to, co faktycznie napisałeś w swoim przykładzie. Cały program w Node jest taki sam jak w przeglądarce-po prostu rejestruje wywołania zwrotne zdarzeń, które są wywoływane asynchronicznie później po zakończeniu głównego programu. Również żaden z uchwytów nie blokuje się, więc żadna funkcja nie zajmuje zauważalnego czasu, aby skończ. Nie jestem pewien, czy rozumiem, co rzeczywiście miał na myśli w swoim pytaniu, ale mam nadzieję, że to, co napisałem będzie pomocne, aby zrozumieć, jak to wszystko działa.

Aktualizacja:

Po przeczytaniu więcej informacji w komentarzach na temat tego, jak wygląda twój program, mogę powiedzieć więcej.

Jeśli twój program to coś w stylu:

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

Wtedy możesz też napisać to tak:

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

Spowoduje, że str wyjdą poza zakres po serwerze.start() jest wywoływany i będzie w końcu zbieraj się. Ponadto sprawi, że Twoje wcięcia będą łatwiejsze do opanowania, czego nie można lekceważyć w przypadku bardziej złożonych programów.

Jeśli chodzi o val możesz w tym przypadku uczynić z niej zmienną globalną, co znacznie uprościłoby Twój kod. Oczywiście nie musisz, możesz zmagać się z zamknięciami, ale w tym przypadku tworzenie val globalnego lub tworzenie go w zewnętrznym zakresie wspólnym zarówno dla ReadFile callback, jak i dla funkcji serverCallback wydaje się najprostszym rozwiązaniem rozwiązanie.

Pamiętaj, że wszędzie, kiedy możesz użyć funkcji anonimowej, możesz również użyć funkcji nazwanej, a z tymi możesz wybrać, w jakim zakresie chcesz, aby żyły.

 4
Author: rsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 10:05:45

Domyślam się, że str nie będzie zbierany jako śmieci, ponieważ może być używany przez restofprogram(). Tak, a str powinien dostać GCed jeśli restofprogram został zadeklarowany na zewnątrz, z wyjątkiem, jeśli zrobisz coś takiego:

function restofprogram(val) { ... }

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

Lub jeśli getvaluefromstr jest zadeklarowany jako coś takiego:

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

Kontynuacja-pytanie: czy v8 robi po prostu GC czy robi kombinację GC i ref. liczenie (jak python?)

 1
Author: dhruvbird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-30 11:40:19