Terminologia związana z pamięcią

Meggin Kearney
Meggin Kearney

Ta sekcja zawiera opis typowych terminów używanych w analizie pamięci i ma zastosowanie do różnych narzędzi do profilowania pamięci w różnych językach.

Opisane tu terminy i pojęcia odnoszą się do narzędzia Chrome DevTools Heap Profiler. Jeśli zdarzyło Ci się już pracować z językiem Java, .NET lub innym programem do profilowania pamięci, ta wiadomość może Ci się przydać.

Rozmiary obiektów

Pamięć to wykres z typami podstawowymi (takimi jak liczby i ciągi) oraz obiektami (tablicami powiązaniowymi). Może to być wykres z szeregiem połączonych ze sobą punktów:

Wizualne przedstawienie wspomnienia

Obiekt może przechowywać pamięć na 2 sposoby:

  • Bezpośrednio przy samym obiekcie.
  • polega na przechowywaniu odwołań do innych obiektów, co zapobiega ich automatycznemu usuwaniu przez moduł do czyszczenia pamięci (GC).

Podczas korzystania z narzędzia Heap Profiler w Narzędziach deweloperskich (którego narzędzie do badania problemów z pamięcią można znaleźć w sekcji „Profiles”) często zaglądasz do różnych kolumn informacji. Dwa wyróżniające się rozmiary to Płytki rozmiar i Zachowany rozmiar. Co oznaczają one?

Płytki i niezakłócony rozmiar

Płytki rozmiar

Jest to rozmiar pamięci przechowywanej przez sam obiekt.

Typowe obiekty JavaScript mają pewną pamięć zarezerwowaną na potrzeby opisu i przechowywania wartości bezpośrednich. Zazwyczaj tylko tablice i ciągi znaków mogą mieć niewielkie rozmiary. Jednak ciągi tekstowe i tablice zewnętrzne mają często główne miejsce na dane w pamięci mechanizmu renderowania, przez co na stercie JavaScriptu pojawia się tylko mały obiekt kodu.

Pamięć mechanizmu renderowania to cała pamięć procesu, w którym renderowana jest sprawdzana strona: pamięć natywna + pamięć JS strony + pamięć sterty JS wszystkich dedykowanych instancji roboczych uruchomionych przez stronę. Jednak nawet mały obiekt może pośrednio przechowywać dużą ilość pamięci, ponieważ uniemożliwia to pozbycie się innych obiektów przez automatyczny proces czyszczenia pamięci.

Zachowany rozmiar

Jest to rozmiar pamięci, który jest uwolniony po usunięciu samego obiektu wraz z jego obiektami zależnymi, które stały się nieosiągalne z katalogów głównych GC.

Dane główne GC składają się z nicków (lokalnych lub globalnych) tworzonych podczas tworzenia odwołania z kodu natywnego do obiektu JavaScript spoza wersji 8. Wszystkie takie uchwyty można znaleźć w zrzucie sterty w sekcji Katalogi główne GC > Zakres uchwytu i Katalogi główne GC > Uchwyty globalne. Opisanie nicków w tej dokumentacji bez zagłębiania się w szczegóły implementacji przeglądarki może być mylące. Nie musisz się przejmować ani poziomami głównymi GC ani uchwytami.

Istnieje wiele wewnętrznych głównych wersji GC, z których większość nie jest interesująca dla użytkowników. Z punktu widzenia aplikacji istnieją następujące rodzaje elementów głównych:

  • Obiekt globalny okna (w każdym elemencie iframe). W zrzutach sterty znajduje się pole odległości, które jest liczbą odwołań do właściwości w najkrótszej ścieżce przechowywania od okna.
  • Drzewo DOM dokumentu zawierające wszystkie natywne węzły DOM, które są osiągalne przez przemierzenie dokumentu. Nie wszystkie mogą mieć opakowania JS, ale jeśli je mają, będą aktywne, gdy dokument będzie aktywny.
  • Czasami obiekty mogą być przechowywane przez kontekst debugera i konsolę Narzędzi deweloperskich (np. po sprawdzeniu w konsoli). Twórz zrzuty sterty z czystą konsolą i bez aktywnych punktów przerwania w debugerze.

Wykres pamięci zaczyna się od elementu głównego, który może być obiektem window przeglądarki lub obiektem Global modułu Node.js. Nie masz kontroli nad tym, jak ten obiekt główny jest przesyłany.

Nie można sterować obiektem głównym

Wszystko, czego nie można uzyskać w katalogu głównym, otrzymuje GC.

Drzewo przechowywania obiektów

Sterta to sieć połączonych ze sobą obiektów. W świecie matematyki taka struktura jest nazywana wykresem, czyli wykresem pamięci. Wykres został utworzony z węzłów połączonych za pomocą krawędzi, które mają określone etykiety.

  • Węzły (lub obiekty) są oznaczone nazwą funkcji konstruktora, która została użyta do ich utworzenia.
  • Krawędzie są oznaczone nazwami właściwości.

Dowiedz się, jak nagrać profil za pomocą narzędzia do profilu sterty. Do wartych uwagi danych, które można zobaczyć w poniższym nagraniu programu Heap Profiler, należą odległość: odległość od pierwiastka GC. Jeśli niemal wszystkie obiekty tego samego typu znajdują się w tej samej odległości, a kilka z nich znajduje się w większej odległości, warto się przyjrzeć temu obszarowi.

Odległość od pierwiastka

Dominatory

Obiekty dominujące mają strukturę drzewa, ponieważ każdy z nich ma dokładnie jedną dominację. Osoba dominująca nad obiektem może nie mieć bezpośrednich odniesień do obiektu, który dominuje. Oznacza to, że drzewo dominujące nie jest rozpinającym się drzewem na wykresie.

Na tym schemacie:

  • Węzeł 1 dominuje w węźle 2
  • Węzeł 2 dominuje w węzłach 3, 4 i 6
  • Węzeł 3 dominuje w węźle 5
  • Węzeł 5 dominuje w węźle 8
  • Węzeł 6 dominuje w węźle 7

Struktura drzewa dominującego

W poniższym przykładzie węzeł #3 jest dominującym elementem #10, ale #7 występuje też w każdej prostej ścieżce od GC do #10. Dlatego obiekt B jest dominującym obiektem A, jeśli istnieje on w każdej prostej ścieżce od korzenia do obiektu A.

Animowana ilustracja dominująca

Informacje o modelu V8

Podczas profilowania pamięci pomocne jest zrozumienie, dlaczego zrzuty sterty wyglądają w określony sposób. W tej sekcji opisujemy niektóre tematy związane z pamięcią, które dotyczą maszyny wirtualnej JavaScript V8 (maszyny wirtualnej V8).

Reprezentacja obiektów JavaScript

Są 3 typy podstawowe:

  • Liczby (np. 3,14159)
  • Wartości logiczne (prawda lub fałsz)
  • Ciągi znaków (np. „Werner Heisenberg”)

Nie mogą się odwoływać do innych wartości i zawsze są liśćmi lub węzłami kończącymi się.

Numery można zapisywać jako:

  • natychmiastowe 31-bitowe wartości całkowite nazywane małymi liczbami całkowitymi (SMIs) lub
  • obiektów sterty nazywanych numerami sterty. Numery sterty służą do przechowywania wartości, które nie mieszczą się w formularzu SMI (np. podwójne) lub gdy wartość należy łączyć (np. przez ustawienie jej właściwości).

Ciągi tekstowe można przechowywać w jednym z tych formatów:

  • stertę maszyn wirtualnych,
  • umieszczone w pamięci mechanizmu renderowania. Tworzony jest obiekt kodu używany do uzyskiwania dostępu do pamięci zewnętrznej, gdzie na przykład przechowywane są źródła skryptów i inne treści odbierane z internetu, a nie kopiowane do sterty maszyn wirtualnych.

Pamięć nowych obiektów JavaScript jest przydzielana z dedykowanej sterty JavaScript (lub sterty maszyn wirtualnych). Te obiekty są zarządzane przez kolektor śmieci V8, dlatego pozostaną aktywne, dopóki będzie mieć do nich co najmniej jedno istotne odwołanie.

Obiekty natywne to wszystko, czego nie ma na stercie JavaScriptu. Obiekt natywny, w przeciwieństwie do obiektu sterty, nie jest zarządzany przez kolektor śmieci V8 przez cały czas swojego działania i można uzyskać do niego dostęp tylko z poziomu JavaScriptu za pomocą obiektu otoki JavaScript.

Ciąg znaków przeciwdziałania to obiekt składający się z par przechowywanych, a następnie połączonych ciągów. Jest to wynik konkatenacji. Zawartość ciągu znaków Cons jest łączona tylko wtedy, gdy jest to konieczne. Przykładem może być utworzenie podłańcucha połączonego ciągu znaków.

Jeśli na przykład połączysz elementy a i b, otrzymasz ciąg znaków (a, b), który reprezentuje wynik konkatenacji. Jeśli później połączysz z tym wynikiem ciąg d, otrzymasz kolejny ciąg przeciwdziałania ((a, b), d.

Tablice – jest to obiekt z kluczami liczbowymi. Są one powszechnie używane w maszynie wirtualnej V8 do przechowywania dużych ilości danych. Zbiory par klucz-wartość używane jak słowniki są obsługiwane przez tablice.

Typowy obiekt JavaScript może być używany do przechowywania jednego z dwóch typów tablic:

  • z właściwościami nazwanymi,
  • elementy liczbowe

Jeśli jest bardzo mała liczba właściwości, mogą być one przechowywane wewnętrznie w samym obiekcie JavaScript.

Mapa – obiekt opisujący rodzaj obiektu i jego układ. Mapy służą na przykład do opisywania niejawnych hierarchii obiektów na potrzeby szybkiego dostępu do właściwości.

Grupy obiektów

Każda grupa obiektów natywnych składa się z obiektów, które łączą się ze sobą nawzajem. Rozważmy na przykład poddrzewo DOM, w którym każdy węzeł ma link ze swoim elementem nadrzędnym oraz z kolejnym elementem podrzędnym i kolejnym, tworząc w ten sposób połączony graf. Pamiętaj, że obiekty natywne nie są reprezentowane na stercie JavaScriptu – dlatego mają zerowy rozmiar. Zamiast tego tworzone są obiekty opakowujące.

Każdy obiekt kodu zawiera odwołanie do odpowiedniego obiektu natywnego na potrzeby przekierowania do niego poleceń. Grupa obiektów sama zawiera obiekty otoki. Nie spowoduje to jednak uruchomienia cyklu, którego nie można pobrać, ponieważ GC jest na tyle sprytny, aby zwolnić grupy obiektów, do których nie można już się odwoływać. Jednak zapomnienie o wydaniu pojedynczego opakowania spowoduje podtrzymanie całej grupy wraz z powiązanymi kodami.