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.
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.
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.
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?)
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