2013-08-06 12 views
6

ja przeglądając slajdy w prezentacji: http://slid.es/gruizdevilla/memoryW jaki sposób zamknięcia tworzą wycieki pamięci?

i na jednym ze slajdów, kod ten jest prezentowany z sugestią, że tworzy przeciek pamięci:

var a = function() { 
    var smallStr = 'x', 
    largeStr = new Array(1000000).join('x'); 

    return function (n) { 
     eval(''); //maintains reference to largeStr 
     return smallStr; 
    }; 
}(); 

Zamknięcia może być inna źródło wycieków pamięci. Dowiedz się, jakie referencje są zachowane w zamknięciu.

I pamiętaj: eval jest zły

Może ktoś wyjaśnić ten problem tutaj?

+3

nie wierzę, że jest wyciek pamięci zdefiniowane przez tę samą prezentację (który powiedział przeciek jest „, gdy program nie zwróci wielokrotnie pamięć uzyskał tymczasowy użytek "), ponieważ nie dzieje się to wielokrotnie. Ale 'largeStr' będzie wiązał fragment pamięci, aż" a "wykracza poza zakres. Również 'eval()' nie jest złe, to prawie zawsze złe narzędzie do tej pracy. – nnnnnn

+0

@nnnnnn: Zwłaszcza tutaj, gdzie 'eval' wydaje się być używany do zapobiegania statycznej analizie kodu, która pozwoliłaby zbieraczowi śmieci na zbieranie' largeStr' nawet wtedy, gdy żywa jest referencja do zwróconej funkcji. – Bergi

+0

@MedicineMan: Czy "* rozumiesz, jakie referencje zostały zachowane w zamknięciu *" czy nie? – Bergi

Odpowiedz

2

Dobrze, rozważmy, co się tutaj stanie;

var a = (function() { // `a` will be set to the return of this function 
    var smallStr = 'x', 
    largeStr = new Array(1000000).join('x'); 

    return function (n) { // which is another function; creating a closure 
     eval(''); 
     return smallStr; 
    }; 
}()); 

funkcji wewnętrznej musi mieć dostęp do wszystkich zmiennych z funkcji zewnętrznego, to znaczy tak długo, jak w odniesieniu do istnieje, zmienne z funkcji zewnętrznej nie może być zbierane śmieci i tym samym kontynuować pamięci po spożyciu zakończyło się wywoływaniem i dlatego może spowodować "wycieki pamięci".

Jeśli masz do czynienia z dużych danych, takich jak ten i skończysz z nim, ustawić go na null

+0

Dobre wyjaśnienie, które powinno być zrozumiałe dla każdego, kto czyta twoją odpowiedź zainteresowaną tym. –

5

Jeśli zamiast wrócić do funkcji, które wykonuje

eval(''); 

powróciłeś taki, który przechodzi jej argument

eval(n); 

wtedy ktoś mógłby nazwać a('largeStr') uzyskać tablicę, więc interpreter JavaScript może nie śmieci zbierać th e tablica.

Tłumacze mógł uświadomić sobie, że

eval(''); 

jest równoważna

; 

ale większość z nich nie są na tyle mądry, aby to zrobić, tak szybko, jak to widzą eval przestają pozwalając GC zamkniętym-over zmienne, o ile zamknięcie jest osiągalne.


Przeciek pamięci pojawia się, gdy eval nie mogą skutecznie przejść przez zamknięte zmiennych ze względu na charakter jego wejścia:

eval('x' + (n-1)); 

Od 'x' + (n-1) nie może wytwarzać ciąg JS odwołujący largeStr brak danych wejściowych może prowadzić do użycia largeStr, ale nadal jest przypięty w pamięci.


Aby zobaczyć całość w działaniu, bawić się z

var f = (function() { 
    var a = [,,,,,]; 
    return function (x) { return eval(x); }; 
    })(); 
alert(f('a.length')); 
+0

@Bergi, naprawiłem tę literówkę. Dzięki. –

0

Załóżmy przepisać kod tylko trochę:

function makeClosure() { 
    var smallStr = 'x', 
    largeStr = new Array(1000000).join('x'); 

    return function (n) { 
     eval(''); //maintains reference to largeStr 
     return smallStr; 
    }; 
} 

var a = makeClosure(); 
assert(a() === 'x'); 

makeClosure zwraca funkcję, stąd a jest funkcjonować. Jednak funkcja ta nadal jest wykonywana w zakresie, w którym została zdefiniowana (to definicja zamknięcia). Jeśli było zrobić:

function makeEnumerator() { 
    var count = 0; 

    return function() { 
    count++; 
    return count; 
    }; 
} 

var enum = makeEnumerator(); 
assert(enum() === 1); 
assert(enum() === 2); 

enum nadal ma dostęp do count. Wracając do naszej sprawy, zamknięcie zachowuje odniesienie do smallStr, które pozostaje w pamięci. largeStr nie został zachowany i powinien zostać zwolniony.

Jednakże, eval działa również w bieżącym zakresie i może używać largeStr. Z tego powodu przeglądarka jest również zmuszona do zachowania largeStr.

Krótko mówiąc, nie należy używać eval :)

Powiązane problemy