2012-01-17 13 views
10

Właśnie pomagałem w udzielaniu wywiadów dla nowego programisty, a JavaScript jest główną częścią mojej roli i roli, którą mamy rekrutacja dla. Szczerze mówiąc, kandydat nie był tak dobry i nie rozumiał języka JavaScript, ale w wywiadzie pomylił JavaScript z C# i zaczął omawiać wycieki pamięci w JS. Chciałem interweniować, ale w tym momencie uświadomiłem sobie, jak niewiele wiem o wyciekach pamięci w JS, poza tym, że zużywają dużo pamięci i spowalniają działanie.Przecieki pamięci w JavaScript: czym one są, jak je wykryć, jak je utworzyć

Myśląc o tym podczas wywiadu, jedyną rzeczą, którą mogę zapamiętać, jest Przewodnik Defender OReilly (myślę, że to była czwarta edycja), wspominając o Mark i Sweep Garbage Collections. Ale od kiedy to czytam, to zanikało i nie mogę tego naprawdę rozwinąć. Znalazłem bardzo mało na ten temat, który jest jasny i zwięzły (poza artykułem Crockforda o tym nie było to jasne).

Czy ktoś może podsumować tak prosto, jak to możliwe: Co to są wycieki pamięci w JS, jak możemy je zauważyć, jak je tworzyć - Piszę JS od lat i to całkowicie zepsuło moją wiedzę i pewność siebie jako Nigdy tak naprawdę o tym nie myślałem!

Odpowiedz

4

Faktycznie, „true” wyciek pamięci nigdy nie powinno być możliwe w języku, który ma automatyczną śmieciarza. Więc jeśli istnieje wyciek pamięci, zawsze jest to błąd w mechanizmie underlayingu (na przykład problem z named function expressions w niektórych IE).

Po tym jak to wyjaśniliśmy, nadal możliwe jest uzyskanie dużej ilości pamięci za pomocą javascript i przytrzymanie go bez zwalniania. Ale to nie jest prawdziwy wyciek pamięci. Na przykład, każdy function call tworzy zamknięcie w ECMAscript. Zamknięcie leksykalne polega między innymi na kopiowaniu odniesienia do danych każdego kontekstu rodzica (obiektów aktywacyjnych i zmiennych). To wymaga trochę pamięci, zwłaszcza jeśli tworzysz wiele zamknięć.

Kolejny przykład ze świata Javascript Javascript: Tworzymy obraz dynamiczny za pomocą new Image() i ustawiamy źródło na duży obraz. Teraz mamy odnośnik do obrazu i nie można uzyskać śmieci zebranych, dopóki wszystkie odniesienia nie znikną lub nie zostaną użyte (nawet jeśli dobre narzędzie pamięci poprawnie podpowie, że pamięć była używana dla obrazów, a nie dla javascript).

Ale tak naprawdę są to jedyne scenariusze, w których naprawdę można "przeciekać" pamięć w tym języku. Ponownie, nie jest to tak naprawdę przeciekająca pamięć jak C malloc(), w której ponownie zapominamy o free() tej sekcji. Ponieważ nie ma dynamicznego zarządzania pamięcią w ECMAscript, to wszystko jest całkowicie poza twoim zasięgiem.

+0

cóż, to przeciek pamięci. To tak, jakby powiedzieć: "zapomnienie o dzwonieniu za darmo to nie przeciek pamięci, to po prostu trzymanie go bez zwalniania" – Raynos

+0

@Raynos: nie jest to inna historia. – jAndy

+0

[Jak się różnią] (https://gist.github.com/1627097) – Raynos

1

Javascript jest zaimplementowany inaczej we wszystkich przeglądarkach. Ale istnieje standard, który powinny spełniać wszystkie przeglądarki: ECMAscript.

Należy wziąć pod uwagę, że wszystkie współczesne języki implementują własne wersje reference counting, więc najlepszym sposobem na uniknięcie wycieków pamięci jest odwołanie wszystkich nieużywanych zmiennych do wartości null.

3
var trolls = (function() { 
    var reallyBigObject = eatMemory(); 

    // make closure (#1) 
    // store reallyBigObject in closure 
    (function() { 
    var lulz = reallyBigObject; 
    })(); 

    // make another closure (#2) 
    return function() { 
    return 42; 
    }; 
})(); 

Można by oczekiwać trolls po prostu być function() { return 42; } i spodziewasz się reallyBigObject być usunięte i śmieci zebrane.

Nie jest, ponieważ jeśli pojedyncze zamknięcie (nr 1) odwołuje się do zmiennej z zewnętrznego zakresu.Następnie wszystkie zamknięcia (oprócz 2) odnoszą się do tej zmiennej.

Tylko dlatego, że masz odniesienie do numeru 2, oznacza to, że masz odniesienie do reallyBigObject, które nie zostanie usunięte, dopóki # 2 nie będzie martwe.

Teraz należy wziąć pod uwagę średnią architekturę zamknięć, w której zawija się wszystko w zamknięciach i zagnieżdża się 10 głębokości. Możesz zobaczyć, jak łatwo jest przechowywać odniesienia do obiektów.

Uwaga powyższe informacje dotyczą wersji 8. Dowolny całkowicie ES5 przeglądarka zgodny będzie wyciekać z

var trolls = (function() { 
    var reallyBigObject = eatMemory(); 
    return function() {}; 
})(); 

Ponieważ każda funkcja wewnętrzna może mieć odniesienie do każdej zmiennej zamykającego określonego w zakresie zewnętrznym, za ES5. Większość przeglądarek przyjmuje skróty i optymalizuje je w sposób, który nie jest zauważalny.

+0

Nie sądzę, że nowoczesne GC są tak głupie. Uwierz mi, aktualne GC bardzo dobrze analizują tego typu rzeczy. Ale i tak powinniśmy przeprowadzić wywiad z @hashedderami. – jAndy

+1

@jAle oni są głupi, tak właśnie działa v8. Każda funkcja wewnętrzna dzieli ten sam obiekt kontekstu zamknięcia. Ten obiekt kontekstu zamknięcia zawiera związek wszystkich zmiennych wymaganych przez wszystkie zamknięcia funkcji. Jest to lepsze niż to, co mówi specyfikacja ES5, specyfikacja ES5 mówi, że każda funkcja wewnętrzna musi przechowywać odniesienie do każdej zmiennej dostępnej w zamknięciu. – Raynos

Powiązane problemy