Rozwiązywanie problemów z pamięcią

Dowiedz się, jak używać Chrome i Narzędzi dla programistów, aby znajdować problemy z pamięcią, które wpływają na wydajność strony, w tym wycieki pamięci, nadmierne zużycie pamięci i częste usuwanie pamięci podręcznej.

Podsumowanie

  • Aby sprawdzić, ile pamięci wykorzystuje Twoja strona, otwórz Menedżera zadań Chrome.
  • Wykorzystanie pamięci można wizualizować na przestrzeni czasu za pomocą nagrań z osi czasu.
  • Za pomocą migawek Heap możesz wykrywać odłączone drzewa DOM (częsta przyczyna wycieków pamięci).
  • Dowiedz się, kiedy w stosie JS jest przydzielana nowa pamięć, dzięki zapisom na osi czasu alokacji.
  • Identyfikowanie odłączonych elementów przechowywanych przez odwołanie JavaScript.

Omówienie

Zgodnie z modelem skuteczności RAIL Twoje działania na rzecz poprawy skuteczności powinny być ukierunkowane na użytkowników.

Problemy z pamięcią są ważne, ponieważ użytkownicy często je zauważają. Użytkownicy mogą odczuwać problemy z pamięcią w takie sposoby:

  • Wydajność strony z upływem czasu stopniowo spada. Może to być objaw wycieku pamięci. Wyciek pamięci występuje, gdy błąd na stronie powoduje, że z czasem strona wykorzystuje coraz więcej pamięci.
  • Wydajność strony jest stale niska. Może to być objaw nadmiernego zużycia pamięci. Zalewanie pamięci to sytuacja, w której strona wykorzystuje więcej pamięci niż jest to konieczne do zapewnienia optymalnej szybkości.
  • Wyświetlanie strony jest opóźnione lub często się zatrzymuje. Może to być objaw częstego usuwania elementów. Zbieranie pamięci to proces, w którym przeglądarka odzyskuje pamięć. O tym, kiedy to nastąpi, decyduje przeglądarka. Podczas tworzenia kolekcji wszystkie skrypty są wstrzymywane. Jeśli przeglądarka często usuwa śmieci, wykonywanie skryptu będzie często wstrzymywane.

Nadmierne zużycie pamięci: ile to jest „zbyt dużo”?

Wyciek pamięci jest łatwy do zdefiniowania. Jeśli witryna zużywa coraz więcej pamięci, oznacza to, że występuje wyciek pamięci. Wypełnianie się pamięci jest jednak trudniejsze do zdiagnozowania. Co oznacza „korzystanie z za dużo pamięci”?

Nie ma tu konkretnych 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 smartfonie z najnowocześniejszym procesorem, może się zawiesić na smartfonie z procesorem niskiej klasy.

Kluczem jest tu zastosowanie modelu RAIL i skupienie się na użytkownikach. Dowiedz się, jakie urządzenia są popularne wśród użytkowników, a następnie przetestuj swoją stronę na tych urządzeniach. Jeśli problem występuje stale, może to oznaczać, że strona przekracza możliwości pamięci tych urządzeń.

Monitorowanie wykorzystania pamięci w czasie rzeczywistym za pomocą Menedżera zadań w Chrome

Rozpocznij analizę problemu z pamięcią od Menedżera zadań w Chrome. Menedżer zadań to monitor w czasie rzeczywistym, który pokazuje, ile pamięci zajmuje dana strona.

  1. Naciśnij Shift + Esc lub otwórz menu główne Chrome i kliknij Więcej narzędzi > Menedżer zadań.

    Otwórz Menedżera zadań.

  2. Kliknij prawym przyciskiem myszy nagłówek tabeli w Menedżerze zadań i włącz opcję Pamięć JavaScript.

    Włączanie pamięci JS w nagłówku Menedżera zadań.

Te 2 kolumny podają różne informacje o sposobie wykorzystywania pamięci przez stronę:

  • Kolumna Wykorzystanie pamięci przedstawia pamięć systemu operacyjnego. Węzły DOM są przechowywane w pamięci systemu operacyjnego. Jeśli ta wartość rośnie, oznacza to, że tworzone są węzły DOM.
  • Kolumna Pamięć JavaScript reprezentuje stos JS. Ta kolumna zawiera 2 wartości. Wartością, która Cię interesuje, jest liczba w nawiasach. Liczba na żywo pokazuje, ile pamięci używają dostępne obiekty na stronie. Jeśli ta liczba rośnie, oznacza to, że tworzone są nowe obiekty lub istniejące obiekty się rozrastają.

    Menedżer zadań z włączonym nagłówkiem pamięci JavaScript.

Wizualizacja wycieków pamięci za pomocą nagrań wydajności

Innym punktem wyjścia do analizy może być panel Skuteczność. Panel Wydajność pomaga wizualizować wykorzystanie pamięci przez stronę na przestrzeni czasu.

  1. Otwórz panel Wydajność w Narzędziach deweloperskich.
  2. Zaznacz pole wyboru Pamięć.
  3. Nagraj film.

Aby zademonstrować nagrywanie pamięci w trybie wydajności, użyj tego kodu:

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 naciśniesz przycisk, do którego odwołuje się kod, do treści dokumentu zostanie dołączonych 10 tysięcy węzłów div, a do tablicy x zostanie dodany ciąg znaków o długości miliona x. Uruchomienie tego kodu spowoduje, że na osi czasu pojawi się nagranie podobne do tego na zrzucie ekranu:

Prosty przykład wzrostu.

Najpierw wyjaśnimy interfejs użytkownika. Wykres HEAP w panelu Przegląd (pod NET) przedstawia stos JS. Pod kartą Przegląd znajduje się karta Odliczanie. Tutaj możesz zobaczyć wykorzystanie pamięci według sterty JS (to samo co wykres STERTA w panelu Przegląd), dokumentów, węzłów DOM, słuchaczy i pamięci GPU. Wyłączenie pola wyboru powoduje ukrycie go na wykresie.

Teraz analiza kodu w porównaniu ze zrzutem ekranu. Jeśli spojrzysz na licznik węzłów (zielony wykres), zobaczysz, że jest on zgodny z kodem. Liczba węzłów zwiększa się w określonych krokach. Możesz założyć, że każde zwiększenie liczby węzłów to wywołanie funkcji grow(). Wykres stosu JS (niebieski wykres) nie jest tak prosty. Zgodnie ze sprawdzonymi metodami pierwszy spadek to w istocie wymuszone usuwanie elementów zbędących (osiągane przez naciśnięcie przycisku usuń elementy zbędne). W miarę trwania nagrywania możesz zauważyć, że rozmiar sterty JS gwałtownie wzrasta. Jest to naturalne i oczekiwane: kod JavaScript tworzy węzły DOM po każdym kliknięciu przycisku i wykonywuje wiele operacji podczas tworzenia ciągu znaków o długości miliona znaków. Najważniejsze jest to, że stos JS kończy się na wyższym poziomie niż na początku (tutaj „początek” to punkt po wymuszonym usuwaniu elementów). W rzeczywistych warunkach, jeśli zauważysz ten wzór zwiększania rozmiaru sterty JS lub rozmiaru węzła, może to oznaczać wyciek pamięci.

Wykrywanie wycieków pamięci z odłączonego drzewa DOM za pomocą obrazu sterty

Węzeł DOM może zostać zebrany tylko wtedy, gdy nie ma do niego żadnych odwołań w drzewie DOM strony ani w kodzie JavaScript. Węzeł jest „odłączony”, gdy zostanie usunięty z drzewa DOM, ale niektóre fragmenty kodu JavaScript nadal się do niego odwołują. Odłączone węzły DOM są częstą przyczyną wycieków pamięci. Z tego sekcji dowiesz się, jak używać profilujących sterty w DevTools do identyfikowania odłączonych węzłów.

Oto prosty przykład oddzielonych 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, na który wskazuje kod, powoduje utworzenie węzła ul z 10 węzłami podrzędnymi li. Te węzły są wywoływane przez kod, ale nie występują w drzewie DOM, więc są odłączone.

Zrzuty sterty to jeden ze sposobów identyfikowania odłączonych węzłów. Jak wskazuje nazwa, obrazy sterty pokazują, jak pamięć jest rozłożona między obiekty JS i węzły DOM Twojej strony w danym momencie.

Aby utworzyć zrzut, otwórz DevTools i otwórz panel Pamięć, kliknij przycisk radiowy Zrzut stosu, a potem naciśnij przycisk Zrób zrzut.

Zaznaczony przycisk Wykonaj zrzut sterty.

Przetworzenie i wczytanie migawki może trochę potrwać. Po zakończeniu procesu wybierz go w panelu po lewej stronie (nazwanym Zrzuty ekranu Heap).

Aby wyszukać oddzielone drzewa DOM, wpisz Detached w polu wejściowym Filtr klasy.

Filtrowanie według odłączonych węzłów.

Rozwiń strzałki, aby zbadać oddzielone drzewo.

Badanie odłączonego drzewa.

Kliknij węzeł, aby go dokładniej zbadać. W panelu 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 węzła. Aby naprawić ten konkretny wyciek pamięci, musisz zbadać kod, który używa funkcji detachedTree, i upewnić się, że usuwa on odwołanie do węzła, gdy nie jest już potrzebne.

Badanie odłączonego węzła.

Wykrywanie wycieków pamięci sterty JS za pomocą harmonogramów alokacji

Oś czasu alokacji to kolejne narzędzie, które może pomóc w wykrywaniu wycieków pamięci w stercie JS.

Aby zademonstrować harmonogram przydziału, użyj tego kodu:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Za każdym razem, gdy naciśniesz przycisk, do którego odwołuje się kod, do tablicy x zostanie dodany ciąg znaków o długości miliona znaków.

Aby nagrać czas trwania alokacji, otwórz DevTools, otwórz panel Pamięć, kliknij przycisk radiowy Alokacje na osi czasu, kliknij przycisk  Nagrywaj, wykonaj działanie, które powoduje wyciek pamięci, a potem kliknij przycisk  Zatrzymaj nagrywanie.

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

nowe alokacje na osi czasu skuteczności,

Niebieskie słupki reprezentują nowe alokacje pamięci. Te nowe przydzielenia pamięci to kandydaci do wycieków pamięci. Możesz powiększyć pasek, aby przefiltrować panel Konstruktor tak, aby wyświetlał tylko obiekty przydzielone w wybranym przedziale czasu.

Powiększona oś czasu przydziału

Rozwiń obiekt i kliknij jego wartość, aby wyświetlić więcej szczegółów w panelu Obiekt. Na przykład na zrzucie ekranu poniżej, gdy wyświetlisz szczegóły obiektu, który został przypisany, zobaczysz, że został on przypisany do zmiennej x w zakresie Window.

Szczegóły obiektu tablicy ciągów znaków.

Badanie przydziału pamięci według funkcji

Aby wyświetlić alokację pamięci według funkcji JavaScript, użyj typu profilu Próbkowanie alokacji w panelu Pamięć.

Profil narzędzia do próbkowania alokacji w panelu Pamięć

  1. Zaznacz opcję Próbkowanie przydziału. Jeśli na stronie jest pracownik, możesz go wybrać jako cel profilowania w oknie Wybierz instancję maszyny wirtualnej JavaScript.
  2. Naciśnij przycisk Start (Rozpocznij).
  3. Wykonaj czynności na stronie, którą chcesz zbadać.
  4. Gdy skończysz, naciśnij przycisk Zatrzymaj.

Narzędzia dla programistów wyświetlają podział alokacji pamięci według funkcji. Widok domyślny to Ciężar (od dołu do góry), który u góry wyświetla funkcje przydzielające najwięcej pamięci.

Strona wyników profilu przydziału

Identyfikowanie obiektów przechowywanych przez odwołanie JS

Profil Odłączone elementy zawiera odłączone elementy, które są przechowywane, ponieważ odwołuje się do nich kod JavaScript.

Aby wyświetlić dokładne węzły HTML i ich liczbę, zarejestruj profil Odłączone elementy.

Przykład profilu odłączonych elementów

częstotliwość czyszczenia pamięci;

Jeśli strona często się zatrzymuje, możesz mieć problemy z usuwaniem elementów.

Aby wykryć częste sprzątania pamięci, możesz użyć Menedżera zadań w Chrome lub nagrań pamięci w Chronologii. W Menedżerze zadań często rosnące i malejące wartości Pamięć lub Pamięć JavaScript wskazują na częste usuwanie elementów z pamięci podręcznej. W nagraniach osi czasu często pojawiające się i znikające słupki w wykresach sterty lub liczby węzłów JS wskazują na częste usuwanie elementów z pamięci podręcznej.

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