2012-04-10 9 views
10

Jednym z reklamowany zalet jQuery.data porównaniu surowych właściwości EXPANDO (arbitralnych atrybutów można przypisać do węzłów DOM) jest to, że jQuery.data jest „bezpieczny z odniesieniami okrągłych i dlatego wolne od wycieków pamięci”. Artykuł z Google zatytułowany "Optimizing JavaScript code" przechodzi w bardziej szczegółowo:Dokładne wyjaśnienie JavaScript <-> DOM emisji okrągły odniesienia

Najczęstszymi wycieki pamięci dla aplikacji internetowych wiąże okrągłe odniesień między silnikiem skryptów JavaScript i C++ obiektów przeglądarek wykonawczego DOM (np pomiędzy JavaScript skrypt silnik i infrastruktura COM przeglądarki Internet Explorer lub między silnikiem JavaScript i Firefox XPCOM).

Wymienia dwa przykłady kształtach odniesienia:

  • elementu DOM → obsługi zdarzeń zakres → zamknięcie → DOM

  • elementu DOM → poprzez EXPANDO → pośredni Przedmiotem → elementu DOM

Jeśli jednak cykl odniesienia między węzłem DOM a obiektem JavaScript powoduje przeciek pamięci, nie oznacza to, że nie jest trywialna procedura obsługi zdarzeń (np. onclick) spowoduje taki wyciek? I nie rozumiem, jak to w ogóle możliwe dla obsługi zdarzeń, aby uniknąć cyklu odniesienia, bo tak jak ja to widzę:

  • Element DOM odwołuje się do procedury obsługi zdarzeń.

  • Program obsługi zdarzeń odwołuje się do DOM (bezpośrednio lub pośrednio). W każdym razie prawie niemożliwe jest uniknięcie odniesienia do window w jakiejkolwiek interesującej procedurze obsługi zdarzenia, poza zapisem pętli setInterval odczytującej akcje z globalnej kolejki.

Czy ktoś może podać dokładne wyjaśnienie problemu z kodem JavaScript DOM ↔ DOM? Rzeczy, które chciałbym wyjaśnić:

  • Jakie przeglądarki są wykonywane? Komentarz w źródle jQuery wyraźnie wspomina o IE6-7, ale artykuł Google sugeruje, że dotyczy to również Firefoksa.

  • Czy właściwości expando i programy do obsługi zdarzeń są w jakiś sposób różne w przypadku wycieków pamięci? A może oba te fragmenty kodu są podatne na ten sam wyciek pamięci?

    // Create an expando that references to its own element. 
    var elem = document.getElementById('foo'); 
    elem.myself = elem; 
    
    // Create an event handler that references its own element. 
    var elem = document.getElementById('foo'); 
    elem.onclick = function() { 
        elem.style.display = 'none'; 
    }; 
    
  • Jeśli pamięć przecieki strona powodu okrągłej odniesienia, czy wyciek nie ustąpią, dopóki cała aplikacja przeglądarka jest zamknięta, czy pamięć jest zwolniona, gdy okno/karta jest zamknięta?

+1

Czy ten kod naprawdę spowodował wyciek pamięci? Nie rozumiem dlaczego. var elem = document.getElementById ('foo'); elem.myself = elem; – CEGRD

Odpowiedz

5

To chyba nie warto odtwarzając całą zawartość tych linków, więc ja proponuję zrobić trochę czytania i spojrzeć na other Google search hits:

javascript, circular references and memory leaks

Do you know what may cause memory leaks in JavaScript?

http://www.ibm.com/developerworks/web/library/wa-memleak/

http://www.ibm.com/developerworks/web/library/wa-sieve/index.html?ca=drs-

http://code.google.com/p/google-web-toolkit/wiki/UnderstandingMemoryLeaks

Najgorsze przecieki pamięci są w IE6 gdzie przecieki są trwałe (nawet po opuszczeniu zaatakowaną stronę internetową). Inne wycieki są generalnie tylko wtedy, gdy jesteś na tej konkretnej stronie i czyszczone po opuszczeniu strony.

Faktem jest, że przeglądarka powinna obsługiwać odwołania kołowe. Powinno być w stanie zobaczyć, że nawet jeśli element DOM jest wciąż przywoływany przez element JavaScript, to sam element JavaScript istnieje tylko dlatego, że element DOM jest wciąż żywy i dlatego nie ma prawdziwego odniesienia zewnętrznego pozostawionego do elementu DOM . Jest to uznanie, że starsze wersje IE były złe. W związku z tym w odwołaniach do kodu, które obejmują procedury obsługi zdarzeń, garbage collector musi być wystarczająco inteligentny, aby wiedzieć, że jedynymi odniesieniami pozostawionymi do elementu DOM w JavaScript są referencje, które same znikałyby po usunięciu elementu DOM i jego procedur obsługi zdarzeń - w ten sposób nie ma prawdziwych zewnętrznych odnośników, więc można bezpiecznie usunąć zarówno element DOM, jak i obsługę zdarzeń. Jest to bardziej skomplikowana wersja ogólnego problemu z odniesieniami kołowymi, które muszą spełniać wszystkie moduły zbierające śmieci, gdzie obiekt A odnosi się do obiektu B, a obiekt B odnosi się do obiektu A, ale żaden inny obiekt nie odnosi się do A ani B, a zatem oba mogą zostać uwolnione. .

jQuery's .data() czyni rzeczy bardziej niezawodnymi, ponieważ starsze wersje IE miały szczególny problem z właściwościami, które zostały dodane do elementu DOM i nie obsłużyły odwołań kołowych prawidłowo zawierających dane w tych właściwościach, a zatem nie uwolniłyby rzeczy, gdy powinien (przeciek). .data() działa w ten sposób, używając tylko jednej dodanej właściwości w elemencie DOM, która jest bezpiecznym, nie przeciekającym ciągiem. Ten ciąg jest następnie kluczem do obiektu JavaScript, który może zawierać wszystkie właściwości, które chciałbyś powiązać z elementem DOM. Ponieważ wszystkie te właściwości są przechowywane w prostym kodzie JavaScript, w którym przeglądarki nie mają okrągłych błędów odsyłających, robienie tego w ten sposób nie powoduje przecieków.

Ważne jest, aby zdać sobie sprawę, że nadal mogą występować odwołania cykliczne i jest to w porządku. Obejście polega na przeniesieniu okrągłych odniesień do miejsca, w którym przeglądarki obsługują je odpowiednio, zamiast umieszczania ich w miejscu, w którym przeglądarki mają błędy.

+0

W związku z tym IE6-7 nie zbiera prawidłowo odwołań cyklicznych między DOM i JS dla właściwości expando (czyli tego, co działa w ".data"). Czy poprawnie zbiera śmieci takie referencje dla programów obsługi zdarzeń? –

+0

@joeyadams - tak myślę - to moje słabe rozumowanie. – jfriend00