Gesamtspeichernutzung Ihrer Webseite mit measureUserAgentSpecificMemory() überwachen

Informationen zum Messen der Arbeitsspeichernutzung Ihrer Webseite in der Produktion, um Regressionen zu erkennen

Brendan Kenny
Brendan Kenny
Ulan Degenbaev
Ulan Degenbaev

Browser verwalten den Arbeitsspeicher von Webseiten automatisch. Wenn eine Webseite ein Objekt erstellt, weist der Browser im Hintergrund einen Teil des Arbeitsspeichers zu, um das Objekt zu speichern. Da Arbeitsspeicher eine begrenzte Ressource ist, führt der Browser eine automatische Speicherbereinigung durch, um zu erkennen, wann ein Objekt nicht mehr benötigt wird, und um den zugrunde liegenden Arbeitsspeicher freizugeben.

Die Erkennung ist jedoch nicht perfekt. Das Halteproblem von Alan Turing hat bewiesen, dass eine perfekte Erkennung unmöglich ist. Daher nähern sich Browser dem Begriff „ein Objekt wird benötigt“ mit dem Begriff „ein Objekt ist erreichbar“ an. Wenn die Webseite ein Objekt nicht über seine Variablen und die Felder anderer erreichbarer Objekte erreichen kann, kann der Browser das Objekt sicher zurückfordern. Der Unterschied zwischen diesen beiden Begriffen führt zu Speicherlecks, wie das folgende Beispiel zeigt.

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

Hier wird das größere Array b nicht mehr benötigt, aber der Browser fordert es nicht zurück, da es im Callback weiterhin über object.b erreichbar ist. So geht der Arbeitsspeicher des größeren Arrays verloren.

Speicherlecks sind im Web weit verbreitet, wie diese Studie zeigt. Sie können leicht entstehen, wenn Sie vergessen, einen Ereignis-Listener abzumelden, versehentlich Objekte aus einem iFrame erfassen, einen Worker nicht schließen oder Objekte in Arrays ansammeln. Wenn eine Webseite Speicherlecks aufweist, steigt die Arbeitsspeichernutzung im Laufe der Zeit und die Webseite erscheint den Nutzern langsam und aufgebläht.

Der erste Schritt zur Lösung dieses Problems ist die Messung. Mit der neuen performance.measureUserAgentSpecificMemory() API können Entwickler die Arbeitsspeichernutzung ihrer Webseiten in der Produktion messen und so Speicher lecks erkennen, die bei lokalen Tests nicht gefunden werden.

Wie unterscheidet sich performance.measureUserAgentSpecificMemory() von der Legacy-API performance.memory?

Wenn Sie mit der vorhandenen nicht standardmäßigen performance.memory API vertraut sind, fragen Sie sich vielleicht, wie sich die neue API davon unterscheidet. Der Hauptunterschied besteht darin, dass die alte API die Größe des JavaScript-Heaps zurückgibt, während die neue API den von der Webseite verwendeten Arbeitsspeicher schätzt. Dieser Unterschied ist wichtig, wenn Chrome denselben Heap mit mehreren Webseiten (oder mehreren Instanzen derselben Webseite) teilt. In solchen Fällen kann das Ergebnis der alten API beliebig abweichen. Da die alte API in implementierungsspezifischen Begriffen wie „Heap“ definiert ist, ist eine Standardisierung unmöglich.

Ein weiterer Unterschied besteht darin, dass die neue API die Speichermessung während der automatischen Speicherbereinigung durchführt. Dadurch wird das Rauschen in den Ergebnissen reduziert, es kann aber eine Weile dauern, bis die Ergebnisse vorliegen. Andere Browser können die neue API auch ohne Garbage Collection implementieren.

Empfohlene Anwendungsfälle

Die Arbeitsspeichernutzung einer Webseite hängt vom Zeitpunkt von Ereignissen, Nutzeraktionen und automatischen Speicherbereinigungen ab. Daher ist die API zur Messung der Speichernutzung für die Aggregation von Daten zur Speichernutzung aus der Produktion vorgesehen. Die Ergebnisse einzelner Aufrufe sind weniger nützlich. Beispiele für Anwendungsfälle:

  • Regressionen während der Einführung einer neuen Version der Webseite erkennen, um neue Speicherlecks zu finden.
  • A/B-Tests für ein neues Feature, um die Auswirkungen auf den Arbeitsspeicher zu bewerten und Speicherlecks zu erkennen.
  • Arbeitsspeichernutzung mit der Sitzungsdauer korrelieren, um das Vorhandensein oder Fehlen von Speicherlecks zu überprüfen.
  • Arbeitsspeichernutzung mit Nutzermesswerten korrelieren, um die Gesamtauswirkungen der Arbeitsspeichernutzung zu verstehen.

Browserkompatibilität

Browser Support

  • Chrome: 89.
  • Edge: 89.
  • Firefox: not supported.
  • Safari: not supported.

Source

Derzeit wird die API nur in Chromium-basierten Browsern ab Chrome 89 unterstützt. Das Ergebnis der API ist stark von der Implementierung abhängig, da Browser Objekte unterschiedlich im Arbeitsspeicher darstellen und die Arbeitsspeichernutzung unterschiedlich schätzen. Browser können einige Speicherbereiche aus der Abrechnung ausschließen, wenn die ordnungsgemäße Abrechnung zu teuer oder nicht möglich ist. Daher können die Ergebnisse nicht zwischen Browsern verglichen werden. Es ist nur sinnvoll, die Ergebnisse für denselben Browser zu vergleichen.

performance.measureUserAgentSpecificMemory() verwenden

Funktionserkennung

Die performance.measureUserAgentSpecificMemory Funktion ist nicht verfügbar oder schlägt mit einem SecurityError fehl, wenn die Ausführungsumgebung die Sicherheitsanforderungen zur Verhinderung von Informationslecks zwischen Ursprüngen nicht erfüllt. Sie basiert auf der Isolierung zwischen Ursprüngen, die eine Webseite durch Festlegen von COOP- und COEP-Headern aktivieren kann.

Die Unterstützung kann zur Laufzeit erkannt werden:

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);
}

Lokales Testen

Chrome führt die Speichermessung während der automatischen Speicherbereinigung durch. Das bedeutet, dass die API das Ergebnisversprechen nicht sofort auflöst, sondern auf die nächste automatische Speicherbereinigung wartet.

Durch Aufrufen der API wird nach einem bestimmten Zeitlimit eine automatische Speicherbereinigung erzwungen. Das Zeitlimit ist derzeit auf 20 Sekunden festgelegt, kann aber auch früher eintreten. Wenn Sie Chrome mit dem --enable-blink-features='ForceEagerMeasureMemory' Befehlszeilen-Flag starten, wird das Timeout auf null reduziert. Das ist nützlich für das lokale Debugging und Testen.

Beispiel

Die empfohlene Verwendung der API besteht darin, einen globalen Arbeitsspeicher-Monitor zu definieren, der die Arbeitsspeichernutzung der gesamten Webseite erfasst und die Ergebnisse zur Aggregation und Analyse an einen Server sendet. Die einfachste Methode ist die regelmäßige Erfassung, z. B. alle M Minuten. Dadurch werden die Daten jedoch verzerrt, da zwischen den Erfassungen Speicherpeaks auftreten können.

Das folgende Beispiel zeigt, wie Sie mit einem Poisson-Prozess unvoreingenommene Speichermessungen durchführen können. Dieser Prozess garantiert, dass Erfassungen zu jedem Zeitpunkt gleich wahrscheinlich sind (Demo, Quelle).

Definieren Sie zuerst eine Funktion, die die nächste Speichermessung mit setTimeout() in einem zufälligen Intervall plant.

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);
}

Die Funktion measurementInterval() berechnet ein zufälliges Intervall in Millisekunden, sodass durchschnittlich alle fünf Minuten eine Messung erfolgt. Weitere Informationen zur Mathematik hinter der Funktion finden Sie unter Exponential verteilung.

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

Schließlich ruft die asynchrone Funktion performMeasurement() die API auf, erfasst das Ergebnis und plant die nächste Messung.

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();
}

Beginnen Sie nun mit der Messung.

// Start measurements.
scheduleMeasurement();

Das Ergebnis kann so aussehen:

// 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']
    },
  ],
}

Die Schätzung der Gesamtspeichernutzung wird im Feld bytes zurückgegeben. Dieser Wert ist stark von der Implementierung abhängig und kann nicht zwischen Browsern verglichen werden. Er kann sich sogar zwischen verschiedenen Versionen desselben Browsers ändern. Der Wert umfasst den JavaScript- und DOM-Arbeitsspeicher aller iFrames, verknüpften Fenster und Web-Worker im aktuellen Prozess.

Die Liste breakdown enthält weitere Informationen zum verwendeten Arbeitsspeicher. Jeder Eintrag beschreibt einen Teil des Arbeitsspeichers und ordnet ihn einer Reihe von Fenstern, iFrames und Workern zu, die durch die URL identifiziert werden. Im Feld types sind die implementierungsspezifischen Speichertypen aufgeführt, die mit dem Arbeitsspeicher verknüpft sind.

Alle Listen müssen allgemein behandelt werden. Annahmen, die auf einem bestimmten Browser basieren, dürfen nicht fest codiert werden. Einige Browser geben beispielsweise eine leere breakdown oder eine leere attribution zurück. Andere Browser geben möglicherweise mehrere Einträge in attribution zurück, was darauf hindeutet, dass sie nicht unterscheiden konnten, welcher dieser Einträge den Arbeitsspeicher besitzt.

Feedback

Die Web Performance Community Group und das Chrome-Team freuen sich über Ihr Feedback zu performance.measureUserAgentSpecificMemory().

Informationen zum API-Design

Funktioniert etwas an der API nicht wie erwartet? Oder fehlen Eigenschaften, die Sie zur Umsetzung Ihrer Idee benötigen? Melden Sie ein Problem mit der Spezifikation im GitHub-Repository performance.measureUserAgentSpecificMemory() oder fügen Sie Ihre Gedanken zu einem vorhandenen Problem hinzu.

Problem mit der Implementierung melden

Haben Sie einen Fehler in der Chrome-Implementierung gefunden? Oder weicht die Implementierung von der Spezifikation ab? Melden Sie einen Fehler unter new.crbug.com. Geben Sie so viele Details wie möglich an, stellen Sie eine einfache Anleitung zur Reproduktion des Fehlers bereit und legen Sie für Components Blink>PerformanceAPIs fest.

Unterstützung zeigen

Planen Sie, performance.measureUserAgentSpecificMemory() zu verwenden? Ihre öffentliche Unterstützung hilft dem Chrome-Team, Funktionen zu priorisieren, und zeigt anderen Browseranbietern, wie wichtig es ist, sie zu unterstützen. Senden Sie einen Tweet an @ChromiumDev und teilen Sie uns mit, wo und wie Sie die API verwenden.

Nützliche Links

Danksagungen

Vielen Dank an Domenic Denicola, Yoav Weiss und Mathias Bynens für die Überprüfung des API-Designs sowie an Dominik Inführ, Hannes Payer, Kentaro Hara und Michael Lippautz für die Codeüberprüfungen in Chrome. Außerdem danke ich Per Parker, Philipp Weis, Olga Belomestnykh, Matthew Bolohan und Neil Mckay für ihr wertvolles Nutzerfeedback, das die API erheblich verbessert hat.