Dowiedz się, jak rejestrować obrazy sterty za pomocą opcji Pamięć > Profile > Obraz sterty, i jak znajdować wycieki pamięci.
Profilator sterty pokazuje rozkład pamięci między obiekty JavaScript Twojej strony i powiązane węzły DOM. Za pomocą tej funkcji możesz robić zrzuty pamięci sterty w JS, analizować wykresy pamięci, porównywać zrzuty i wykrywać wycieki pamięci. Więcej informacji znajdziesz w artykule Drzewo obiektów.
Zrób zdjęcie
Aby wykonać zrzut sterty:
- Na stronie, którą chcesz przeanalizować, otwórz Narzędzia deweloperskie i otwórz panel Pamięć.
- Wybierz typ profilowania Zrzut stosu, a następnie wybierz wystąpienie maszyny wirtualnej JavaScript i kliknij Zrób zrzut.
Gdy panel Pamięć wczyta i przeanalizuje obraz sterty, wyświetli łączny rozmiar dostępnych obiektów JavaScript pod tytułem obrazu w sekcji OBRAZY STERTY.
Zrzuty ekranu zawierają tylko obiekty z grafu pamięci, które są dostępne z obiektu globalnego. Tworzenie zrzutu zawsze zaczyna się od usuwania elementów zbędących.
Wyczyść zrzuty
Aby usunąć wszystkie migawki, kliknij
Wyczyść wszystkie profile:Wyświetl zrzuty
Aby sprawdzać migawki z różnych perspektyw i do różnych celów, wybierz jeden z widoków w menu u góry:
Wyświetl | Treść | Cel |
---|---|---|
Podsumowanie | Obiekty pogrupowane według nazw konstruktorów. | Za jego pomocą możesz wyszukiwać obiekty i ich wykorzystanie pamięci na podstawie typu. Przydatne do śledzenia wycieków danych w DOM. |
Porównanie | Różnice między 2 zrzutami | Użyj go do porównania co najmniej 2 migawek (przed i po wykonaniu operacji). Aby sprawdzić, czy występuje wyciek pamięci i jaka jest jego przyczyna, sprawdź różnicę w ilości zwolnionej pamięci i liczbie odwołań. |
Ograniczenie | Zawartość stosu | Zapewnia lepszy widok struktury obiektów i pomaga analizować obiekty, do których odwołuje się globalna przestrzeń nazw (okno), aby znaleźć to, co je utrzymuje. Możesz go używać do analizowania zamknięć i szczegółowego badania obiektów. |
Statystyki | Wykres kołowy alokacji pamięci | Zobacz względne rozmiary części pamięci przydzielonych do kodu, ciągów znaków, tablic JS, tablic typowych i obiektów systemowych. |
Widok podsumowania
Najpierw w widoku Podsumowanie otworzy się migawka stosu, która w kolumnie zawiera listę konstruktorów. Możesz rozwinąć konstruktory, aby zobaczyć instancje obiektów.
Aby odfiltrować nieistotne konstruktory, wpisz nazwę, którą chcesz sprawdzić, w filtrze Klasa u góry widoku Podsumowanie.
Liczby obok nazw konstruktorów wskazują łączną liczbę obiektów utworzonych za pomocą konstruktora. Widok Podsumowanie zawiera też te kolumny:
- Odległość pokazuje odległość od korzenia, korzystając z najkrótszej prostej ścieżki przez węzły.
- Shallow size (płytki rozmiar) to suma płytkich rozmiarów wszystkich obiektów utworzonych przez dany konstruktor. Rozmiar płytki to rozmiar pamięci przechowywanej przez sam obiekt. Zwykle tablice i ciągi mają większy rozmiar płytki. Zobacz też Rozmiary obiektów.
- Zachowany rozmiar to maksymalny rozmiar zachowany w tym samym zbiorze obiektów. Zatrzymany rozmiar to rozmiar pamięci, który możesz zwolnić, usuwając obiekt i uniemożliwiając dostęp do jego elementów zależnych. Zobacz też Rozmiary obiektów.
Gdy rozwiniesz konstruktor, w widoku Podsumowanie zobaczysz wszystkie jego wystąpienia. Każda instancja ma w odpowiednich kolumnach podział na rozmiary płytkie i zatrzymane. Liczba po znaku @
to unikalny identyfikator obiektu. Umożliwia porównywanie zrzutów stosu na podstawie poszczególnych obiektów.
Filtry konstruktora
Widok Podsumowanie umożliwia filtrowanie konstruktorów na podstawie typowych przypadków nieefektywnego wykorzystania pamięci.
Aby użyć tych filtrów, w najbardziej wysuniętym w prawo menu na pasku działań wybierz jedną z tych opcji:
- Wszystkie obiekty: wszystkie obiekty zarejestrowane przez bieżący zrzut. Ustaw domyślnie.
- 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 migawkami 1 i 2: wyświetl różnicę między obiektami w najnowszym i poprzednim migawce. Każdy nowy zrzut ekranu powoduje dodanie kolejnej wartości tego filtra do listy rozwijanej.
- Zduplikowane ciągi: wartości ciągu, które zostały zapisane w pamięci wielokrotnie.
- Obiekty zachowane przez odłączone węzły: obiekty, które są utrzymywane, ponieważ odłączony węzeł DOM się do nich odwołuje.
- Obiekty zachowane przez konsolę Narzędzi deweloperskich: obiekty przechowywane w pamięci, ponieważ zostały ocenione lub z nimi przeprowadzono interakcję w konsoli Narzędzi deweloperskich.
Specjalne wpisy w sekcji Podsumowanie
Oprócz grupowania według konstruktorów widok Podsumowanie grupuje obiekty również według:
- Wbudowane funkcje, takie jak
Array
lubObject
. - Elementy HTML pogrupowane według tagów, np.
<div>
,<a>
,<img>
i inne. - Funkcje zdefiniowane w kodzie.
- Specjalne kategorie, które nie są oparte na konstruktorach.
(array)
Ta kategoria obejmuje różne wewnętrzne obiekty podobne do tablic, które nie odpowiadają bezpośrednio obiektom widocznym w JavaScript.
Na przykład zawartość obiektów JavaScript Array
jest przechowywana w dodatkowym obiekcie wewnętrznym o nazwie (object elements)[]
, aby ułatwić zmianę rozmiaru. Podobnie nazwane właściwości w obiektach JavaScriptu są często przechowywane w dodatkowych wewnętrznych obiektach o nazwie (object properties)[]
, które są też wymienione w kategorii (array)
.
(compiled code)
Ta kategoria obejmuje dane wewnętrzne, których V8 potrzebuje do uruchamiania funkcji zdefiniowanych przez JavaScript lub WebAssembly. Każdą funkcję można przedstawić na wiele sposobów, od małych i wolnych do dużych i szybkich.
V8 automatycznie zarządza wykorzystaniem pamięci w tej kategorii. Jeśli funkcja jest wykonywana wiele razy, V8 przeznacza na nią więcej pamięci, aby mogła działać szybciej. Jeśli funkcja nie była wykonywana przez jakiś czas, V8 może usunąć jej dane wewnętrzne.
(concatenated string)
Gdy V8 koniunguje 2 ciągi znaków, np. za pomocą operatora JavaScript +
, może wewnętrznie przedstawić wynik jako „koniungowany ciąg znaków”, czyli strukturę danych Rope.
Zamiast kopiować wszystkie znaki z 2 ciągów znaków źródłowych do nowego ciągu, V8 przydziela mały obiekt z polami wewnętrznymi o nazwach first
i second
, które wskazują na 2 ciągi znaków źródłowych. Dzięki temu V8 oszczędza czas i pamięć. Z punktu widzenia kodu JavaScript są to zwykłe ciągi znaków, które zachowują się jak każdy inny ciąg znaków.
InternalNode
Ta kategoria obejmuje 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:
- Otwórz Narzędzia deweloperskie i włącz Ustawienia > Eksperymenty > Pokaż opcję, aby wyświetlić informacje wewnętrzne w migawkach stosu.
- Otwórz panel Pamięć, wybierz Zrzut Heap i włącz Udostępnij dane wewnętrzne (w tym dodatkowe szczegóły charakterystyczne dla implementacji).
- Odtwórz problem, który spowodował, że
InternalNode
zatrzymało dużo pamięci. - Wykonaj zrzut sterty. Na tym zrzucie obiekty mają nazwy klas C++, a nie
InternalNode
.
(object shape)
Jak opisano w artykule Fast Properties in V8 (w języku angielskim), V8 śledzi ukryte klasy (lub kształty), aby można było efektywnie przedstawiać wiele obiektów o tych samych właściwościach w tym samym porządku. Ta kategoria zawiera ukryte klasy o nazwie system / Map
(niezwiązane z JavaScriptem Map
) i powiązane z nimi dane.
(sliced string)
Gdy V8 musi pobrać podciąg, np. gdy kod JavaScript wywołuje funkcję String.prototype.substring()
, V8 może przydzielić obiekt podcięty ciąg znaków zamiast kopiować wszystkie odpowiednie znaki z pierwotnego ciągu znaków. Ten nowy obiekt zawiera wskaźnik do oryginalnego ciągu znaków i opisuje, który zakres znaków z pierwotnego ciągu ma być użyty.
Z punktu widzenia kodu JavaScript są to zwykłe ciągi znaków, które zachowują się jak każdy inny ciąg znaków. Jeśli pokrojowy ciąg znaków zajmuje dużo pamięci, program może wywołać problem 2869 i można podjąć celowe działania, aby „spłaszczyć” pokrojowy ciąg znaków.
system / Context
Obiekty wewnętrzne typu system / Context
zawierają zmienne lokalne z closure, czyli zakresu JavaScriptu, do którego może mieć dostęp funkcja zagnieżdżona.
Każda instancja funkcji zawiera wewnętrzny wskaźnik do Context
, w którym jest wykonywana, dzięki czemu może uzyskiwać dostęp do tych zmiennych. Chociaż obiekty Context
nie są widoczne bezpośrednio w JavaScript, masz nad nimi bezpośrednią kontrolę.
(system)
Ta kategoria zawiera różne obiekty wewnętrzne, które nie zostały (jeszcze) sklasyfikowane w żaden bardziej znaczący sposób.
Porównanie
Widok Porównanie pozwala znaleźć wycieki danych, porównując ze sobą różne migawki. Na przykład wykonanie czynności i jej cofnięcie, np. otwarcie dokumentu i jego zamknięcie, nie powinno pozostawiać dodatkowych obiektów.
Aby sprawdzić, czy dana operacja nie powoduje wycieków:
- Przed wykonaniem operacji wykonaj zrzut sterty.
- Wykonywanie operacji. Oznacza to, że musisz wejść w interakcję ze stroną w sposób, który Twoim zdaniem może spowodować wyciek.
- Wykonaj odwrotną operację. Oznacza to, że należy wykonać odwrotną interakcję i powtórzyć ją kilka razy.
- Zrób drugi zrzut ekranu stosu i zmień jego widok na Porównanie, aby porównać go z zrzutem ekranu 1.
Widok Porównanie pokazuje różnicę między 2 migawkami. Po rozwinięciu rekordu sumy wyświetlane są dodane i usunięte wystąpienia obiektu:
Widok Zawieranie
Widok Zawartość to „widok z lotu ptaka” struktury obiektów aplikacji. Umożliwia ona zaglądanie do funkcji zamykających, obserwowanie wewnętrznych obiektów maszyny wirtualnej, które razem tworzą obiekty JavaScript, oraz określanie, ile pamięci zużywa aplikacja na bardzo niskim poziomie.
Widok zawiera kilka punktów wejścia:
- obiekty DOMWindow, Obiekty globalne dla kodu JavaScript.
- GC roots. korzenie GC używane przez zbieracz śmieci maszyny wirtualnej. Korzenie GC mogą składać się z wbudowanych map obiektów, tabel symboli, stosów wątków maszyny wirtualnej, pamięci podręcznej kompilacji, zakresów uchwytów i uchwytów globalnych.
- obiekty natywne, Obiekty przeglądarki „przesyłane” do maszyny wirtualnej JavaScriptu, aby umożliwić automatyzację, np. węzły DOM i reguły CSS.
Sekcja Aparaty retencyjne
Sekcja Zatrzymacze u dołu panelu Pamięć zawiera obiekty, które wskazują obiekt wybrany w widoku. Gdy wybierzesz inne obiekty w dowolnym widoku (z wyjątkiem widoku Statystyki), panel Pamięć zaktualizuje sekcję Zatrzymanie.
W tym przykładzie wybrany ciąg znaków jest przechowywany przez właściwość x
instancji Item
.
Ignorowanie elementów zachowujących
Możesz ukryć elementy stałe, aby sprawdzić, czy inne obiekty nie zastępują wybranego. Dzięki tej opcji nie musisz najpierw usuwać tego elementu z kodu, a potem ponownie robić zrzut pamięci.
Aby ukryć załącznik, kliknij prawym przyciskiem myszy i wybierz Ignoruj ten załącznik. Ignorowane pozycje stałe są oznaczone jako ignored
w kolumnie Odległość. Aby przestać ignorować wszystkie rezerwacje, na górze na pasku czynności kliknij Przywróć zignorowane rezerwacje.
Znajdowanie konkretnego obiektu
Aby znaleźć obiekt w zbiorzonej stercie, możesz użyć skrótu Ctrl + F i wpisać identyfikator obiektu.
Nazywanie funkcji w celu odróżnienia ich od funkcji zamykających
Nazwa funkcji bardzo pomaga w odróżnianiu zamknięć na zrzucie.
Na przykład w tym kodzie nie używamy funkcji nazwanych:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function() { // this is NOT a named function
return largeStr;
};
return lC;
}
Ten przykład:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function lC() { // this IS a named function
return largeStr;
};
return lC;
}
Odkrywanie wycieków danych do DOM
Profilator sterty może odzwierciedlać dwukierunkowe zależności między obiektami natywnymi przeglądarki (węzłami DOM i regułami CSS) a obiektami JavaScript. Pomaga to wykrywać niewidoczne wycieki danych spowodowane zapomnianymi, oderwanymi poddrzewami DOM.
Wycieki danych do DOM mogą być większe, niż Ci się wydaje. Rozważ ten przykład. Kiedy odbierane są odpady #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
#leaf
przechowuje odwołanie do swojego rodzica (parentNode
) i rekursywnie do #tree
, więc dopiero gdy leafRef
zostanie unieważniony, cały drzewo pod #tree
będzie kandydatem na GC.