Monitoruj całkowite wykorzystanie pamięci przez strony internetowe za pomocą narzędziameasureUserAgentSpecMemory()

Dowiedz się, jak mierzyć wykorzystanie pamięci przez stronę internetową w wersji produkcyjnej w celu wykrywania regresji.

Brendan Kenny
Brendan Kenny
Ulan Degenbaev
Ulan Degenbaev

Przeglądarki automatycznie zarządzają pamięcią stron internetowych. Za każdym razem, gdy strona internetowa tworzy obiekt, przeglądarka przydzieli mu fragment pamięci „niestandardowo” do i przechowywać obiekt. Pamięć jest skończonym zasobem, więc przeglądarka czyszczenia pamięci, aby wykryć, kiedy obiekt nie jest już potrzebny, i zwolnić go bazowego fragmentu pamięci.

Wykrywanie nie jest jednak idealne, została udowodniona, że idealne wykrywanie jest zadaniem niemożliwym. Dlatego przeglądarki traktują w przybliżeniu pojęcie „obiektu jest potrzebna” gdzie obiekt jest osiągalny. Jeśli strona internetowa nie może i docierać do obiektu za pomocą jego zmiennych i pól innych osiągalnych obiektów, przeglądarka może bezpiecznie odzyskać obiekt. Różnica między tymi prowadzi do wycieków pamięci, co ilustruje następujący przykład.

const object = {a: new Array(1000), b: new Array(2000)};
setInterval(() => console.log(object.a), 1000);

W tym przypadku większa tablica b nie jest już potrzebna, ale przeglądarka nie Odzyska go, ponieważ jest nadal dostępny przez object.b w wywołaniu zwrotnym. Zatem spowoduje wyciek pamięci większej tablicy.

Wycieki pamięci są powszechne w internecie. Wprowadzenie tej funkcji jest proste – zapominasz wyrejestrować detektor zdarzeń, przez przypadek przechwytywanie obiektów z elementu iframe przez niezamykanie instancji roboczej przez kumulowanie obiektów w tablicach itd. Jeśli strona internetowa ma wyciek pamięci, wykorzystanie pamięci rośnie z czasem, a strona działa wolno na większą skalę.

Pierwszym krokiem do rozwiązania tego problemu jest jego pomiar. Nowy performance.measureUserAgentSpecificMemory() API umożliwia programistom: mierzyć wykorzystanie pamięci przez strony internetowe w środowisku produkcyjnym i w ten sposób wykrywać pamięć. wycieku, które przechodzą lokalne testy.

Czym performance.measureUserAgentSpecificMemory() różni się od starszej wersji interfejsu API performance.memory?

Jeśli znasz już niestandardowy interfejs API performance.memory, możesz zastanawiać się, czym różni się od niego nowy interfejs API. Główna różnica polega na tym, że stary interfejs API zwraca rozmiar stosu JavaScript, a nowy szacuje ilość pamięci wykorzystywanej przez stronę internetową. Ta różnica zmienia się ważne, gdy Chrome współdziele tę samą stertę z wieloma stronami internetowymi (lub wiele wystąpień tej samej strony internetowej). W takich przypadkach wynikiem starego argumentu Interfejs API może być dowolnie wyłączony. Stary interfejs API został zdefiniowany w pojęć związanych z implementacją, takich jak „sterta”, ustandaryzowanie tych rozwiązań jest beznadziejne.

Kolejna różnica polega na tym, że nowy interfejs API dokonuje pomiaru pamięci podczas czyszczenia pamięci. Pozwala to zmniejszyć szum w wynikach, ale może to zająć do czasu uzyskania wyników. Pamiętaj, że inne przeglądarki mogą zdecydować się wdrożyć nowy interfejs API bez konieczności czyszczenia pamięci.

Sugerowane zastosowania

Wykorzystanie pamięci przez stronę internetową zależy od czasu wystąpienia zdarzeń, działań użytkownika do czyszczenia pamięci. Dlatego interfejs API do pomiaru pamięci jest przeznaczony agregacji danych o wykorzystaniu pamięci z środowiska produkcyjnego. wyniki poszczególnych rozmów. są mniej przydatne. Przykładowe przypadki użycia:

  • wykrywanie regresji podczas wdrażania nowej wersji strony internetowej w celu wychwytywania nowych wycieków pamięci;
  • Testy A/B nowej funkcji pozwalające ocenić jej wpływ na pamięć i wykryć jej wycieki.
  • Korelowanie wykorzystania pamięci z czasem trwania sesji w celu sprawdzenia obecności lub braku wycieków pamięci.
  • Powiązanie wykorzystania pamięci ze wskaźnikami o użytkownikach w celu poznania ogólnego wpływu wykorzystania pamięci.

Zgodność z przeglądarką

Obsługa przeglądarek

  • Chrome: 89.
  • Edge: 89.
  • Firefox: funkcja nieobsługiwana.
  • Safari: nieobsługiwane.

Źródło

Obecnie ten interfejs API jest obsługiwany tylko w przeglądarkach opartych na Chromium (od Chrome 89). wynik działania interfejsu API jest w dużym stopniu zależny od wdrożenia, ponieważ przeglądarki na różne sposoby przedstawiania obiektów w pamięci szacuję wykorzystanie pamięci. Przeglądarki mogą wykluczyć niektóre regiony pamięci z czy odpowiednie rozliczanie jest zbyt kosztowne lub niewykonalne. Wyniki nie można porównywać w różnych przeglądarkach. Istotne jest wyłącznie porównanie wyników dla tej samej przeglądarki.

Jak korzystać z aplikacji performance.measureUserAgentSpecificMemory()

Wykrywanie cech

Funkcja performance.measureUserAgentSpecificMemory będzie niedostępna lub może wystąpi błąd SecurityError, jeśli środowisko wykonawcze nie zostanie wypełnione. wymagania dotyczące bezpieczeństwa związane z zapobieganiem wyciekom informacji z innych domen. Opiera się na izolacji zasobów z innych domen, co strona internetowa może aktywować. przez ustawienie nagłówków COOP+COEP.

Wsparcie można wykryć w czasie działania:

if (!window.crossOriginIsolated) {
  console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
} else if (!performance.measureUserAgentSpecificMemory) {
  console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
} else {
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
    } else {
      throw error;
    }
  }
  console.log(result);
}

Testy lokalne

Chrome mierzy pamięć podczas czyszczenia pamięci, co oznacza, że że interfejs API nie realizuje od razu obietnicy wyników i czeka na potrzeby kolejnego czyszczenia pamięci.

Wywołanie interfejsu API wymusza odśmiecanie pamięci po upływie czasu oczekiwania, który jest jest ustawiony na 20 sekund, ale może nastąpić wcześniej. Uruchamianie Chrome na Flaga wiersza poleceń --enable-blink-features='ForceEagerMeasureMemory' ogranicza limit czasu wynosi zero i jest przydatny przy lokalnym debugowaniu i testowaniu.

Przykład

Zalecanym zastosowaniem interfejsu API jest zdefiniowanie globalnego monitora pamięci, który próbkuje wykorzystanie pamięci dla całej strony internetowej i wysyła wyniki na serwer do agregacji i analizy. Najprostszym sposobem jest próbkowanie okresowo, przez przykładowy tekst co M min. Powoduje to jednak uprzedzenia danych, ponieważ między próbkami mogą wystąpić szczyty pamięci.

Ten przykład pokazuje, jak obiektywnych pomiarów pamięci przy użyciu procesu Poissona, który gwarantuje, że prawdopodobieństwo pojawienia się próbek w dowolnym momencie jest jednakowe (demonstracja, źródło).

Najpierw zdefiniuj funkcję, która planuje następny pomiar pamięci za pomocą funkcji setTimeout() z interwałem losowym.

function scheduleMeasurement() {
  // Check measurement API is available.
  if (!window.crossOriginIsolated) {
    console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
    console.log('See https://web.dev/coop-coep/ to learn more')
    return;
  }
  if (!performance.measureUserAgentSpecificMemory) {
    console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
    return;
  }
  const interval = measurementInterval();
  console.log(`Running next memory measurement in ${Math.round(interval / 1000)} seconds`);
  setTimeout(performMeasurement, interval);
}

Funkcja measurementInterval() oblicza losowy interwał w milisekundach dzięki czemu średnio co 5 minut jest wykonywany 1 pomiar. Zobacz Wykładnicze rozkładu, jeśli interesuje Cię funkcja matematyczna.

function measurementInterval() {
  const MEAN_INTERVAL_IN_MS = 5 * 60 * 1000;
  return -Math.log(Math.random()) * MEAN_INTERVAL_IN_MS;
}

Na koniec asynchroniczna funkcja performMeasurement() wywołuje interfejs API, rekordy i zaplanuje następny pomiar.

async function performMeasurement() {
  // 1. Invoke performance.measureUserAgentSpecificMemory().
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
      return;
    }
    // Rethrow other errors.
    throw error;
  }
  // 2. Record the result.
  console.log('Memory usage:', result);
  // 3. Schedule the next measurement.
  scheduleMeasurement();
}

Na koniec rozpocznij pomiary.

// Start measurements.
scheduleMeasurement();

Wynik może wyglądać tak:

// Console output:
{
  bytes: 60_100_000,
  breakdown: [
    {
      bytes: 40_000_000,
      attribution: [{
        url: 'https://example.com/',
        scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 20_000_000,
      attribution: [{
          url: 'https://example.com/iframe',
          container: {
            id: 'iframe-id-attribute',
            src: '/iframe',
          },
          scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 100_000,
      attribution: [],
      types: ['DOM']
    },
  ],
}

Szacowane całkowite wykorzystanie pamięci jest zwracane w polu bytes. Ta wartość to zależą od implementacji i nie można ich porównywać w różnych przeglądarkach. Może nawet zmieniać wersję tej samej przeglądarki. Wartość obejmuje Pamięć JavaScript i DOM wszystkich elementów iframe, powiązanych okien i instancji roboczych w w bieżącym procesie.

Lista breakdown zawiera dodatkowe informacje o używanej pamięci. Każdy wpis opisuje część pamięci i przypisuje ją do zbioru okien, elementów iframe i instancji roboczych identyfikowanych przez adres URL. Pole types zawiera listę typy pamięci w danej implementacji powiązane z pamięcią.

Ważne jest, aby wszystkie listy traktować jako ogólne i nie kodować na stałe dla konkretnej przeglądarki. Na przykład niektóre przeglądarki mogą zwraca pusty atrybut breakdown lub attribution. Inne przeglądarki mogą zwracają w funkcji attribution wiele wpisów wskazujących, że nie mogą ich rozróżnić który z tych wpisów jest właścicielem pamięci.

Prześlij opinię

Zespół Web Performance Community Group i zespół Chrome chętnie aby poznać Twoją opinię i doświadczenia performance.measureUserAgentSpecificMemory()

Opowiedz nam o konstrukcji interfejsu API

Czy jest coś, co w interfejsie API nie działa zgodnie z oczekiwaniami? Czy są brakuje właściwości, które są niezbędne do realizacji pomysłu? Zgłoś problem ze specyfikacją dotyczącą performance.measureUserAgentSpecificMemory() na GitHubie lub dodać do swoje przemyślenia na temat istniejącego problemu.

Zgłoś problem z implementacją

Czy wystąpił błąd z implementacją Chrome? Czy wdrożenie różni się od specyfikacji? Zgłoś błąd na new.crbug.com. Koniecznie Podaj jak najwięcej szczegółów, podaj proste instrukcje odtwarzania filmu, błąd i ustaw opcję Komponenty na Blink>PerformanceAPIs. Usługa Glitch świetnie nadaje się do szybkiego i łatwego udostępniania poprawek.

Pokaż wsparcie

Czy zamierzasz korzystać z usługi performance.measureUserAgentSpecificMemory()? Twoje poparcie publiczne pomaga zespołowi Chrome ustalać priorytety funkcji i pokazuje innym dostawcom przeglądarek, jest ich wsparcie. Wyślij tweeta na adres @ChromiumDev i daj nam znać, gdzie i jak go używasz.

Przydatne linki

Podziękowania

Dziękujemy Domenic Denicola, Yoav Weissowi i Mathiasowi Bynensowi za recenzje projektów API. oraz Dominik Inführ, Hannes Payer, Kentaro Hara, Michael Lippautz w celu weryfikacji kodu. w Chrome. Dziękuję również Per Parker, Philipp Weis, Olga Belomestnykh, Matthew Bolohan i Neil Mckay za przekazanie cennych opinii użytkowników, i usprawniliśmy interfejs API.

Baner powitalny, autor: Harrison Broadbent, Unsplash