Rejestruj zrzuty sterty

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

Dowiedz się, jak tworzyć zrzuty stosu w sekcji Pamięć > Profile > Zrzut stosu i znajdować wycieki pamięci.

Program profilujący sterty pokazuje rozkład pamięci według obiektów JavaScript na Twojej stronie i powiązanych węzłów DOM. Za jego pomocą możesz robić zrzuty stosu JS, analizować wykresy pamięci, porównywać zrzuty i znajdować wycieki pamięci. Więcej informacji znajdziesz w artykule Drzewo przechowywania obiektów.

Zrób zrzut

Aby zrobić zrzut stosu:

  1. Na stronie, którą chcesz profilować, otwórz Narzędzia deweloperskie i otwórz panel Pamięć.
  2. Wybierz typ profilowania radio_button_checked Zrzut sterty, a następnie wybierz instancję maszyny wirtualnej JavaScript i kliknij Wykonaj zrzut.

Wybrany typ profilowania i instancja maszyny wirtualnej JavaScript.

Gdy panel Pamięć wczytuje i analizuje zrzut, pod tytułem zrzutu w sekcji SNAPSHOTS pod tytułem zrzutu ekranu wyświetla się łączny rozmiar osiągalnych obiektów JavaScript.

Łączny rozmiar osiągalnych obiektów.

Zrzuty pokazują tylko te obiekty z grafu pamięci, które są osiągalne z obiektu globalnego. Wykonywanie zrzutu zawsze rozpoczyna się od czyszczenia pamięci.

Migawka stosu rozproszonych obiektów Item.

Wyczyść zrzuty

Aby usunąć wszystkie zrzuty, kliknij zablokuj Wyczyść wszystkie profile:

Wyczyść wszystkie profile.

Wyświetl zrzuty

Aby przejrzeć migawki z różnych perspektyw w różnych celach, wybierz jeden z widoków z menu u góry:

Wyświetl Treści Purpose
Podsumowanie Obiekty pogrupowane według nazw konstruktorów. Dzięki niemu możesz wyszukiwać obiekty i wykorzystywać ich pamięć na podstawie ich typu. Przydatne do śledzenia wycieków DOM.
Porównanie Różnice między dwoma zrzutami. Służy do porównywania co najmniej 2 zrzutów przed operacją i po niej. Potwierdź obecność i przyczynę wycieku pamięci, sprawdzając wartość delta wolnej pamięci i liczby odwołań.
izolacja Zawartość sterty Zapewnia lepszy widok struktury obiektów i pomaga analizować obiekty, do których odwołuje się globalna przestrzeń nazw (window), w celu ustalenia, co je chroni. Użyj go, aby przeanalizować zamknięcia i zająć się obiektami na bardzo ogólnym poziomie.
Statystyki Wykres kołowy alokacji pamięci Sprawdzanie rzeczywistych rozmiarów części pamięci przydzielonych do kodu, ciągów znaków, tablic JS, tablic z typem i obiektów systemowych.

Widok podsumowania wybrany z menu u góry.

Widok podsumowania

Początkowo w widoku Podsumowanie otwierany jest zrzut stosu z listą Konstruktorów w kolumnie. Możesz rozwijać konstruktory, aby zobaczyć obiekty, których instancja została utworzona.

Widok podsumowania z rozwiniętym konstruktorem.

Aby odfiltrować nieistotne konstruktory, wpisz nazwę, którą chcesz sprawdzić, w Filtrze zajęć u góry widoku Podsumowanie.

Liczby obok nazw konstruktorów wskazują łączną liczbę obiektów utworzonych przy użyciu tego konstruktora. Widok Podsumowanie zawiera też te kolumny:

  • Odległość pokazuje odległość do pierwiastka przy użyciu najkrótszej prostej ścieżki węzłów.
  • Płytki rozmiar pokazuje sumę płytkich rozmiarów wszystkich obiektów utworzonych przez określony konstruktor. Płytki rozmiar to rozmiar pamięci przechowywanej przez sam obiekt. Tablice i ciągi tekstowe mają zazwyczaj płytki rozmiar. Zobacz też Rozmiary obiektów.
  • Zachowywany rozmiar pokazuje maksymalny rozmiar zachowany w tym samym zbiorze obiektów. Zachowany rozmiar to rozmiar pamięci, który można zwolnić przez usunięcie obiektu, przez co elementy zależne nie są już osiągalne. Zobacz też Rozmiary obiektów.

Po rozwinięciu konstruktora widok Podsumowanie pokazuje wszystkie jego instancje. W przypadku każdej instancji w odpowiednich kolumnach zobaczysz podział na jej płytki i zachowywany rozmiar. Liczba po znaku @ to unikalny identyfikator obiektu. Umożliwia porównywanie zrzutów sterty według poszczególnych obiektów.

Filtry konstruktora

Widok Podsumowanie umożliwia filtrowanie konstruktorów na podstawie typowych przypadków niewydajnego wykorzystania pamięci.

Aby użyć tych filtrów, w menu po prawej stronie na pasku działań wybierz jedną z tych opcji:

  • Wszystkie obiekty: wszystkie obiekty przechwycone przez bieżący zrzut. Ustawienie domyślne.
  • Obiekty przydzielone przed zrzutem 1: obiekty, które zostały utworzone i pozostały w pamięci przed wykonaniem pierwszego zrzutu.
  • Obiekty alokowane między zrzutami 1 i zrzutami 2: wyświetla różnicę w obiektach między najnowszym a poprzednim zrzutem ekranu. Każdy nowy zrzut dodaje do listy przyrost wartości tego filtra.
  • Zduplikowane ciągi: wartości ciągów znaków, które zostały zapisane wiele razy w pamięci.
  • Obiekty przechowywane przez odłączone węzły: obiekty, które są utrzymywane, ponieważ odwołują się do nich odłączony węzeł DOM.
  • Obiekty przechowywane przez konsolę Narzędzi deweloperskich: obiekty są przechowywane w pamięci, ponieważ były oceniane lub akceptowane za pomocą konsoli Narzędzi deweloperskich.

Wpisy specjalne w podsumowaniu

Oprócz grupowania według konstruktorów widok Podsumowanie grupuje też obiekty według:

  • Wbudowane funkcje, np. Array lub Object.
  • Funkcje zdefiniowane w kodzie.
  • Kategorie specjalne, które nie są oparte na konstruktorach.

Wpisy konstruktora.

(array)

Ta kategoria zawiera różne obiekty tablicowe wewnętrznych, które nie odpowiadają bezpośrednio obiektom widocznym w JavaScripcie.

Na przykład zawartość obiektów JavaScript Array jest przechowywana w dodatkowym obiekcie wewnętrznym o nazwie (object elements)[], co ułatwia zmianę rozmiaru. Podobnie nazwane właściwości w obiektach JavaScriptu są często przechowywane w dodatkowych obiektach wewnętrznych o nazwie (object properties)[], które są też wymienione w kategorii (array).

(compiled code)

Ta kategoria zawiera dane wewnętrzne, których V8 potrzebuje do uruchamiania funkcji zdefiniowanych przez JavaScript lub WebAssembly. Każdą funkcję można przedstawić na różne sposoby: od małej i wolnej do dużej i szybkiej.

V8 automatycznie zarządza wykorzystaniem pamięci w tej kategorii. Jeśli funkcja jest uruchamiana wiele razy, V8 wykorzystuje na potrzeby tej funkcji więcej pamięci, aby działać szybciej. Jeśli funkcja nie była uruchomiona od jakiegoś czasu, V8 może wyczyścić dla niej dane wewnętrzne.

(concatenated string)

Gdy V8 łączy 2 ciągi, np. w przypadku operatora + w języku JavaScript, może reprezentować wynik wewnętrznie jako „połączony ciąg znaków”, nazywany też strukturą danych Rope.

Zamiast kopiować wszystkie znaki z dwóch ciągów źródłowych do nowego ciągu, V8 przypisuje niewielki obiekt z wewnętrznymi polami o nazwach first i second, które wskazują te dwa ciągi źródłowe. To pozwala V8 zaoszczędzić czas i pamięć. Z punktu widzenia kodu JavaScript są to zwykłe ciągi znaków, które działają jak każdy inny ciąg.

InternalNode

Ta kategoria reprezentuje obiekty przydzielone poza V8, takie jak obiekty C++ zdefiniowane przez Blink.

Aby wyświetlić nazwy klas C++, użyj Chrome for Testing i wykonaj te czynności:

  1. Otwórz Narzędzia deweloperskie i włącz ustawienia Ustawienia > Eksperymenty > pole wyboru Pokaż opcję wyświetlania zasobów wewnętrznych w zrzutach stosu.
  2. Otwórz panel Pamięć, wybierz radio_button_checked Zrzut sterty i włącz radio_button_checked Udostępnij wewnętrzne elementy (zawiera dodatkowe szczegóły dotyczące implementacji).
  3. Odtwórz problem, który sprawiał, że InternalNode zachowuje dużo pamięci.
  4. Zrób zrzut stosu. W tym zrzucie obiekty zamiast InternalNode mają nazwy klas C++.
(object shape)

Jak opisano w artykule Szybkie właściwości w wersji 8, V8 śledzi klasy ukryte (lub kształty), aby można było skutecznie przedstawić wiele obiektów o tych samych właściwościach w tej samej kolejności. Ta kategoria zawiera ukryte klasy system / Map (niezwiązane z JavaScriptem Map) oraz powiązane dane.

(sliced string)

Gdy V8 musi przejąć podłańcuch, np. gdy kod JavaScript wywołuje String.prototype.substring(), V8 może zdecydować się na przydzielenie obiektu wyciętego ciągu zamiast kopiowania wszystkich odpowiednich znaków z pierwotnego ciągu. Ten nowy obiekt zawiera wskaźnik do pierwotnego ciągu znaków i opisuje, jakiego zakresu znaków z pierwotnego ciągu ma użyć.

Z punktu widzenia kodu JavaScript są to zwykłe ciągi znaków, które działają jak każdy inny ciąg. Jeśli pocięty ciąg zachowuje dużą ilość pamięci, być może program wywołał problem 2869 i skuteczniej przyniosło to skutek w celu „spłaszczenia” tego fragmentu.

system / Context

Obiekty wewnętrzne typu system / Context zawierają zmienne lokalne z zamknięcia – zakresu JavaScriptu, do którego ma dostęp funkcja zagnieżdżona.

Każda instancja funkcji zawiera wewnętrzny wskaźnik do funkcji Context, która jest wykonywana, by umożliwić dostęp do tych zmiennych. Obiekty Context nie są widoczne bezpośrednio z JavaScriptu, ale masz nad nimi bezpośrednią kontrolę.

(system)

Ta kategoria zawiera różne obiekty wewnętrzne, które nie zostały (jeszcze) przypisane do bardziej istotnej kategorii.

Porównanie

Widok Porównanie umożliwia znajdowanie obiektów, które wyciekły, przez porównywanie ze sobą kilku zrzutów. Na przykład wykonanie działania i cofnięcie go, np. otwarcie i zamknięcie dokumentu, nie powinno pozostawiać po sobie żadnych dodatkowych obiektów.

Aby sprawdzić, czy określona operacja nie powoduje wycieków:

  1. Przed wykonaniem operacji zrób zrzut stosu.
  2. Wykonaj operację. Oznacza to, że wejdź w interakcję ze stroną w taki sposób, który Twoim zdaniem może powodować wyciek.
  3. Wykonaj operację odwrotną. czyli wykonaj odwrotną interakcję i powtórz tę czynność kilka razy.
  4. Zrób drugi zrzut stosu i zmień jego widok na Porównanie i porównaj go ze Zrzutem 1.

Widok Porównanie pokazuje różnicę między dwoma zrzutami. Po rozwinięciu wpisu wyświetlają się dodane i usunięte instancje obiektów:

Porównanie ze zrzutem 1.

Widok kontenera

Widok Kontener to „widok z lotu ptaka” struktury obiektów aplikacji. Pozwala zajrzeć do wnętrza funkcji, obserwować wewnętrzne obiekty maszyn wirtualnych, które razem składają się na obiekty JavaScript, oraz określić, ile pamięci używa Twoja aplikacja na bardzo niskim poziomie.

Widok ten zawiera kilka punktów wejścia:

  • obiekty DOMWindow. Globalne obiekty kodu JavaScript.
  • Elementy główne GC. Katalogi główne GC używane przez moduł do czyszczenia pamięci maszyny wirtualnej. Elementy główne GC mogą zawierać wbudowane mapy obiektów, tabele symboli, stosy wątków maszyn wirtualnych, pamięci podręczne kompilacji, zakresy uchwytów i uchwyty globalne.
  • Obiekty natywne. Obiekty przeglądarki „przesunięte” do maszyny wirtualnej JavaScriptu, aby umożliwić automatyzację, np. węzły DOM i reguły CSS.

Widok kontenera.

Sekcja elementów zachowujących

W sekcji Elementy składowe u dołu panelu Pamięć znajdują się obiekty wskazujące obiekt wybrany w widoku. Gdy wybierzesz inne obiekty w dowolnym widoku oprócz widoku Statystyki, panel Pamięć aktualizuje sekcję Elementy składowe.

Sekcja Elementy zachowujące.

W tym przykładzie wybrany ciąg jest przechowywany przez właściwość x instancji Item.

Ignoruj elementy zachowujące

Możesz ukryć elementy zachowujące, aby sprawdzić, czy inne obiekty zachowują wybrany. Dzięki tej opcji nie musisz najpierw usuwać jej z kodu, a potem jeszcze raz robić zrzutu stosu.

Wybierz w menu opcję „Ignoruj tę osobę utrzymującą”.

Aby ukryć element zachowujący, kliknij prawym przyciskiem myszy i wybierz Ignoruj tego członka grupy. Zignorowane osoby zachowujące są oznaczone jako ignored w kolumnie Odległość. Aby nie ignorować wszystkich użytkowników, kliknij playlist_remove Przywróć zignorowane elementy zachowujące na pasku działań u góry.

Znajdowanie konkretnego obiektu

Aby znaleźć obiekt w zebranej stercie, możesz wyszukać go za pomocą Ctrl + F i wpisać identyfikator obiektu.

Nazwij funkcje, aby odróżnić zamknięcia

Warto nazwać funkcje, aby rozróżnić zamknięcia na zrzucie ekranu.

Na przykład ten kod nie korzysta z funkcji nazwanych:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function() { // this is NOT a named function
    return largeStr;
  };

  return lC;
}

W tym przykładzie:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function lC() { // this IS a named function
    return largeStr;
  };

  return lC;
}

Funkcja nazwana w zamknięciu.

Wykrywanie wycieków DOM

Program profilujący sterty może odzwierciedlać dwukierunkowe zależności między obiektami natywnymi dla przeglądarki (węzły DOM i reguły CSS) a obiektami JavaScript. Pomaga to wykryć niewidoczne wyciek danych wynikające z unoszących się wokół zapomnianych, odłączonych poddrzew DOM.

Wycieki DOM mogą być większe, niż Ci się wydaje. Przyjrzyjmy się temu przykładowi. Kiedy jest odprowadzany śmieci w: #tree?

  var select = document.querySelector;
  var treeRef = select("#tree");
  var leafRef = select("#leaf");
  var body = select("body");

  body.removeChild(treeRef);

  //#tree can't be GC yet due to treeRef
  treeRef = null;

  //#tree can't be GC yet due to indirect
  //reference from leafRef

  leafRef = null;
  //#NOW #tree can be garbage collected

Funkcja #leaf zachowuje odniesienie do elementu nadrzędnego (parentNode) i powtarza się aż do wartości #tree, więc tylko wtedy, gdy wartość leafRef ma wartość null, całe drzewo w elemencie #tree będzie kandydatem do GC.

Poddrzewa DOM