Rozwiązywanie problemów z pamięcią

Naucz się, jak używać Chrome’a i narzędzi DevTools do znajdowania problemów z pamięcią, które wpływają na wydajność strony, w tym wycieków pamięci, rozdęcia pamięci i częstego zbierania śmieci.

Podsumowanie #

  • Dowiedz się, ile pamięci zużywa obecnie twoja strona, korzystając z Menedżera zadań Chrome.
  • Wizualizuj użycie pamięci w czasie dzięki nagraniom na osi czasu.
  • Zidentyfikuj odłączone drzewa DOM (częsta przyczyna wycieków pamięci) dzięki Heap Snapshots.
  • Dowiedz się, kiedy nowa pamięć jest alokowana w stercie JS dzięki nagraniom osi czasu alokacji.

Przegląd #

W duchu modelu wydajności RAIL, w centrum uwagi Twoich wysiłków związanych z wydajnością powinni być użytkownicy.

Problemy z pamięcią są ważne, ponieważ są często zauważalne przez użytkowników. Użytkownicy mogą postrzegać problemy z pamięcią na następujące sposoby:

  • Wydajność strony pogarsza się stopniowo w czasie. Jest to prawdopodobnie symptom wycieku pamięci. Wyciek pamięci jest wtedy, gdy błąd na stronie powoduje, że strona stopniowo zużywa coraz więcej pamięci w czasie.
  • Wydajność strony jest stale zła. Jest to prawdopodobnie symptom rozdęcia pamięci. Rozdęcie pamięci jest wtedy, gdy strona używa więcej pamięci niż jest to konieczne dla optymalnej prędkości strony.
  • Wydajność strony jest opóźniona lub wydaje się często zatrzymywać. Jest to prawdopodobnie symptom częstego zbierania śmieci. Garbage collection to moment, w którym przeglądarka odzyskuje pamięć. Przeglądarka decyduje, kiedy to nastąpi. Podczas zbierania śmieci, wszystkie wykonania skryptów są wstrzymywane. Więc jeśli przeglądarka dużo zbiera śmieci, wykonywanie skryptów będzie często wstrzymywane.

Zawieszenie pamięci: ile to jest „za dużo”?

Przeciek pamięci jest łatwy do zdefiniowania. Jeśli strona stopniowo używa coraz więcej pamięci, to masz wyciek. Ale rozdęcie pamięci jest nieco trudniejsze do określenia. Co kwalifikuje się jako „używanie zbyt dużej ilości pamięci”?

Nie ma tutaj twardych liczb, ponieważ różne urządzenia i przeglądarki mają różne możliwości. Ta sama strona, która działa płynnie na high-endowym smartfonie, może się zawiesić na smartfonie low-end.

Kluczem jest tutaj użycie modelu RAIL i skupienie się na użytkownikach. Dowiedz się, jakie urządzenia są popularne wśród Twoich użytkowników, a następnie przetestuj swoją stronę na tych urządzeniach. Jeśli doświadczenie jest konsekwentnie złe, strona może przekraczać możliwości pamięciowe tych urządzeń.

Monitoruj użycie pamięci w czasie rzeczywistym za pomocą Menedżera zadań Chrome #

Użyj Menedżera zadań Chrome jako punktu wyjścia do zbadania problemu z pamięcią. Menedżer zadań to monitor w czasie rzeczywistym, który mówi, ile pamięci aktualnie używa strona.

  1. Naciśnij Shift+Esc lub przejdź do menu głównego Chrome i wybierz Więcej narzędzi > Menedżer zadań, aby otworzyć Menedżera zadań.

    Otwarcie Menedżera zadań

  2. Kliknij prawym przyciskiem myszy na nagłówku tabeli w Menedżerze zadań i włącz pamięć JavaScript.

    Włączenie pamięci JS

Te dwie kolumny mówią różne rzeczy o tym, jak Twoja strona używa pamięci:

  • Kolumna Pamięć reprezentuje pamięć natywną. Węzły DOM są przechowywane w pamięci natywnej. Jeśli ta wartość rośnie, węzły DOM są tworzone.
  • Kolumna Pamięć JavaScript reprezentuje stertę JS. Ta kolumna zawiera dwie wartości. Wartość, którą jesteś zainteresowany to liczba live (liczba w nawiasie). Liczba live reprezentuje ilość pamięci, jaką wykorzystują osiągalne obiekty na twojej stronie. Jeśli ta liczba rośnie, to albo tworzone są nowe obiekty, albo istniejące obiekty rosną.

Wizualizuj wycieki pamięci za pomocą nagrań z osi czasu #

Możesz również użyć panelu osi czasu jako kolejnego punktu wyjścia w swoim śledztwie. Panel Timeline pomoże Ci zwizualizować użycie pamięci przez stronę w czasie.

  1. Otwórz panel Timeline w DevTools.
  2. Włącz pole wyboru Memory.
  3. Wykonaj nagranie.

Porada: Dobrą praktyką jest rozpoczęcie i zakończenie nagrywania z wymuszonym zbieraniem śmieci. Kliknij przycisk zbierania śmieci (przycisk wymuszania zbierania śmieci) podczas nagrywania, aby wymusić zbieranie śmieci.

Aby zademonstrować zapisy pamięci Timeline, rozważ poniższy kod:

var x = ;
function grow() {
for (var i = 0; i < 10000; i++) {
document.body.appendChild(document.createElement('div'));
}
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);

Za każdym razem, gdy przycisk, do którego odwołuje się kod, zostanie naciśnięty, dziesięć tysięcy div węzłów zostanie dołączonych do ciała dokumentu, a ciąg o długości miliona x znaków zostanie wepchnięty do tablicy x. Po uruchomieniu tego kodu powstaje nagranie osi czasu jak na poniższym zrzucie ekranu:

przykładowy przykład wzrostu

Na początek wyjaśnienie interfejsu użytkownika. Wykres HEAP w panelu Przegląd (poniżej NET) reprezentuje stertę JS. Poniżej panelu przeglądania znajduje się panel licznika. Tutaj możesz zobaczyć zużycie pamięci w rozbiciu na stertę JS (tak samo jak wykres HEAP w panelu Przegląd), dokumenty, węzły DOM, słuchaczy i pamięć GPU. Wyłączenie pola wyboru ukrywa je z wykresu.

A teraz analiza kodu w porównaniu do zrzutu ekranu. Jeśli spojrzysz na licznik węzłów (zielony wykres), możesz zobaczyć, że jest on zgodny z kodem. Liczba węzłów wzrasta w dyskretnych krokach. Można przypuszczać, że każdy wzrost liczby węzłów jest wywołaniem grow(). Wykres sterty JS (niebieski wykres) nie jest tak prosty. Zgodnie z najlepszymi praktykami, pierwsze zanurzenie jest w rzeczywistości wymuszonym odśmiecaniem (osiągniętym przez naciśnięcie przycisku collect garbage). W miarę postępu nagrywania można zauważyć, że rozmiar sterty JS gwałtownie wzrasta. Jest to naturalne i oczekiwane: kod JavaScript tworzy węzły DOM przy każdym kliknięciu przycisku i wykonuje dużo pracy, gdy tworzy ciąg o długości miliona znaków. Kluczową rzeczą jest tutaj fakt, że sterta JS kończy się wyżej niż się zaczęła („początek” tutaj to punkt po wymuszonym odśmiecaniu). W prawdziwym świecie, jeśli zobaczyłbyś ten wzorzec zwiększania rozmiaru sterty JS lub rozmiaru węzła, potencjalnie oznaczałoby to wyciek pamięci.

Odkryj wycieki pamięci odłączonego drzewa DOM za pomocą Heap Snapshots #

Węzeł DOM może być odśmiecany tylko wtedy, gdy nie ma do niego żadnych odniesień ani w drzewie DOM strony, ani w kodzie JavaScript. Mówi się, że węzeł jest „odłączony”, gdy jest usunięty z drzewa DOM, ale jakiś JavaScript wciąż się do niego odwołuje. Odłączone węzły DOM są częstą przyczyną wycieków pamięci. Ta sekcja nauczy Cię jak używać profilerów sterty DevTools do identyfikowania odłączonych węzłów.

Tutaj znajduje się prosty przykład odłączonych węzłów DOM.

var detachedTree;
function create() {
var ul = document.createElement('ul');
for (var i = 0; i < 10; i++) {
var li = document.createElement('li');
ul.appendChild(li);
}
detachedTree = ul;
}
document.getElementById('create').addEventListener('click', create);

Kliknięcie przycisku, do którego odwołuje się kod, tworzy węzeł ul z dziesięcioma dziećmi li. Węzły te są przywoływane przez kod, ale nie istnieją w drzewie DOM, więc są odłączone.

Zrzuty sterty są jednym ze sposobów identyfikacji odłączonych węzłów. Jak sama nazwa wskazuje, zrzuty sterty pokazują, w jaki sposób pamięć jest dystrybuowana pomiędzy obiektami JS i węzłami DOM strony w momencie wykonania zrzutu.

Aby utworzyć zrzut, otwórz DevTools i przejdź do panelu Profiles, wybierz przycisk radiowy Take Heap Snapshot, a następnie naciśnij przycisk Take Snapshot.

take heap snapshot

Zrzut może zająć trochę czasu na przetworzenie i załadowanie. Po jego zakończeniu wybierz go z lewego panelu (o nazwie HEAP SNAPSHOTS).

W polu tekstowym Class filter wpisz Detached, aby wyszukać odłączone drzewa DOM.

filtrowanie dla odłączonych węzłów

Rozwiń karaty, aby zbadać odłączone drzewo.

badanie odłączonego drzewa

Węzły zaznaczone na żółto mają bezpośrednie odniesienia do nich z kodu JavaScript. Węzły podświetlone na czerwono nie mają bezpośrednich referencji. Są one żywe tylko dlatego, że są częścią drzewa żółtego węzła. Ogólnie rzecz biorąc, chcesz się skupić na żółtych węzłach. Popraw swój kod tak, aby żółty węzeł nie był żywy dłużej, niż jest to konieczne, a także pozbądź się czerwonych węzłów, które są częścią drzewa żółtego węzła.

Kliknij na żółty węzeł, aby go dokładniej zbadać. W okienku Obiekty możesz zobaczyć więcej informacji o kodzie, który się do niego odwołuje. Na przykład, na poniższym zrzucie ekranu widać, że zmienna detachedTree odwołuje się do tego węzła. Aby naprawić ten konkretny wyciek pamięci, należy przestudiować kod, który używa detachedTree i upewnić się, że usuwa on swoje odniesienie do węzła, gdy nie jest on już potrzebny.

badanie żółtego węzła

Identyfikacja wycieków pamięci sterty JS za pomocą Allocation Timelines #

Alocation Timeline to kolejne narzędzie, które może pomóc w śledzeniu wycieków pamięci w stercie JS.

Aby zademonstrować Allocation Timeline rozważ następujący kod:

var x = ;
function grow() {
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);

Za każdym razem, gdy przycisk, do którego odwołuje się kod, jest wciśnięty, do tablicy x dodawany jest ciąg o długości miliona znaków.

Aby nagrać Allocation Timeline, otwórz DevTools, przejdź do panelu Profiles, wybierz przycisk radiowy Record Allocation Timeline, naciśnij przycisk Start, wykonaj akcję, którą podejrzewasz o spowodowanie wycieku pamięci, a następnie naciśnij przycisk zatrzymania nagrywania (stop recording button), gdy skończysz.

Podczas nagrywania zauważ, czy na osi czasu alokacji pojawiają się niebieskie paski, jak na poniższym zrzucie ekranu.

nowe alokacje

Te niebieskie paski reprezentują nowe alokacje pamięci. Te nowe alokacje pamięci są twoimi kandydatami do wycieków pamięci. Możesz powiększyć pasek, aby przefiltrować panel Konstruktora i pokazać tylko obiekty, które zostały zaalokowane w określonym przedziale czasowym.

powiększ oś czasu alokacji

Rozwiń obiekt i kliknij na jego wartość, aby zobaczyć więcej szczegółów na jego temat w panelu obiektów. Na przykład, na poniższym zrzucie ekranu, przeglądając szczegóły nowo przydzielonego obiektu, można zobaczyć, że został on przydzielony do zmiennej x w zakresie Window.

szczegóły obiektu

Zbadaj alokację pamięci według funkcji #

Użyj typu Record Allocation Profiler, aby wyświetlić alokację pamięci według funkcji JavaScript.

Record Allocation Profiler

  1. Wybierz przycisk radiowy Record Allocation Profiler. Jeśli na stronie znajduje się robot, możesz wybrać go jako cel profilowania używając rozwijanego menu obok przycisku Start.
  2. Naciśnij przycisk Start.
  3. Wykonaj działania na stronie, które chcesz zbadać.
  4. Naciśnij przycisk Stop, gdy zakończysz wszystkie swoje działania.

DevTools pokazuje podział alokacji pamięci według funkcji. Domyślnym widokiem jest Ciężki (Bottom Up), który wyświetla funkcje, które przydzieliły najwięcej pamięci na górze.

Profil alokacji

Spotkaj częste odśmiecanie #

Jeśli Twoja strona wydaje się często pauzować, to możesz mieć problemy z odśmiecaniem.

Możesz użyć Menedżera zadań Chrome lub nagrań pamięci na osi czasu, aby zauważyć częste odśmiecanie. W Menedżerze zadań często rosnące i malejące wartości Pamięci lub Pamięci JavaScript oznaczają częste zbieranie śmieci. W nagraniach Osi czasu często rosnące i malejące wykresy sterty JS lub liczby węzłów wskazują na częste zbieranie śmieci.

Po zidentyfikowaniu problemu możesz użyć nagrania Osi czasu alokacji, aby dowiedzieć się, gdzie alokowana jest pamięć i które funkcje powodują alokacje.

W celu wykrycia częstego zbierania śmieci możesz użyć nagrania Osi czasu alokacji.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *