Gesamtspeichernutzung Ihrer Webseite mit measureUserAgentSpecificMemory() überwachen

Hier erfahren Sie, wie Sie die Arbeitsspeichernutzung Ihrer Webseite in der Produktion messen, um Regressionen zu erkennen.

Brendan Kenny
Brendan Kenny
Ulaan Djegenbajew
Ulaan Djegenbajew

Browser verwalten den Arbeitsspeicher von Webseiten automatisch. Immer wenn eine Webseite ein Objekt erstellt, weist der Browser einen Speicherblock zu, um das Objekt zu speichern. Da der Arbeitsspeicher eine begrenzte Ressource ist, führt der Browser eine automatische Speicherbereinigung aus, um zu erkennen, wann ein Objekt nicht mehr benötigt wird, und den zugrunde liegenden Speicherblock freizugeben.

Die Erkennung ist jedoch nicht perfekt. Es hat sich gezeigt, dass eine perfekte Erkennung gar nicht möglich ist. Daher nähern sich Browser dem Konzept „Ein Objekt ist erforderlich“ mit der Vorstellung von „Ein Objekt ist erreichbar“ an. Wenn die Webseite ein Objekt über ihre Variablen und die Felder anderer erreichbarer Objekte nicht erreichen kann, kann der Browser das Objekt sicher zurückfordern. Der Unterschied zwischen diesen beiden Konzepten führt zu Speicherlecks, wie im folgenden Beispiel veranschaulicht.

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

In diesem Fall wird das größere Array b nicht mehr benötigt, aber der Browser ruft es nicht zurück, da es immer noch über object.b im Callback erreichbar ist. Dadurch tritt ein Speicherleck im größeren Array auf.

Speicherlecks sind im Web weit verbreitet. Ein solcher Listener lässt sich leicht einführen, da Sie vergessen, die Registrierung eines Event-Listeners aufzuheben. Dazu werden z. B. Objekte aus einem iFrame versehentlich erfasst, ein Worker nicht geschlossen oder Objekte in Arrays angesammelt. Wenn bei einer Webseite Speicherlecks auftreten, wächst ihre Arbeitsspeichernutzung mit der Zeit, wodurch die Webseite langsam und aufgebläht erscheint.

Der erste Schritt zur Lösung dieses Problems besteht darin, sie zu messen. Mit der neuen performance.measureUserAgentSpecificMemory() API können Entwickler die Arbeitsspeichernutzung ihrer Webseiten in der Produktion messen und so Speicherlecks erkennen, die bei lokalen Tests auftreten.

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

Wenn Sie mit der vorhandenen nicht standardmäßigen performance.memory API vertraut sind, fragen Sie sich vielleicht, wie sich die neue API von ihr 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 genutzten 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 willkürlich abweichen. Da die alte API in implementierungsspezifischen Begriffen wie „Heap“ definiert ist, ist eine Standardisierung hoffnungslos.

Ein weiterer Unterschied besteht darin, dass die neue API Speichermessungen während der automatischen Speicherbereinigung durchführt. Dadurch wird das Rauschen in den Ergebnissen reduziert. Es kann jedoch eine Weile dauern, bis die Ergebnisse vorliegen. Andere Browser implementieren die neue API möglicherweise ohne die automatische Speicherbereinigung.

Empfohlene Anwendungsfälle

Die Arbeitsspeichernutzung einer Webseite hängt vom Zeitpunkt von Ereignissen, Nutzeraktionen und automatischen Speicherbereinigungen ab. Aus diesem Grund ist die Memory Measurement API zum Aggregieren von Speichernutzungsdaten aus der Produktion vorgesehen. Die Ergebnisse einzelner Anrufe sind weniger nützlich. Beispiele für Anwendungsfälle:

  • Regressionserkennung während der Einführung einer neuen Version der Webseite, um neue Speicherlecks zu erkennen.
  • A/B-Tests einer neuen Funktion, um ihre Auswirkungen auf den Arbeitsspeicher zu bewerten und Speicherlecks zu erkennen.
  • Korrelieren der Arbeitsspeichernutzung mit der Sitzungsdauer, um das Vorhandensein oder das Fehlen von Speicherlecks zu prüfen.
  • Korrelieren der Arbeitsspeichernutzung mit Nutzermesswerten, um die Auswirkungen der Arbeitsspeichernutzung insgesamt zu verstehen.

Browserkompatibilität

Unterstützte Browser

  • 89
  • 89
  • x
  • x

Quelle

Derzeit wird die API ab Chrome 89 nur in Chromium-basierten Browsern unterstützt. Das Ergebnis der API ist stark von der Implementierung abhängig, da Browser Objekte im Arbeitsspeicher auf unterschiedliche Weise darstellen und die Speichernutzung auf unterschiedliche Weise schätzen. Browser schließen möglicherweise einige Arbeitsspeicherregionen von der Berücksichtigung aus, wenn eine ordnungsgemäße Erfassung zu teuer oder nicht umsetzbar ist. Daher können die Ergebnisse nicht browserübergreifend verglichen werden. Ein Vergleich der Ergebnisse für denselben Browser ist nur sinnvoll.

performance.measureUserAgentSpecificMemory() verwenden

Funktionserkennung

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

Unterstützung kann während der 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 Arbeitsspeichermessung während der automatischen Speicherbereinigung durch. Das bedeutet, dass die API das Ergebnisversprechen nicht sofort auflöst und stattdessen auf die nächste automatische Speicherbereinigung wartet.

Durch den Aufruf der API wird nach einem Zeitlimit, das derzeit auf 20 Sekunden festgelegt ist, eine automatische Speicherbereinigung erzwungen. Dies kann jedoch auch früher erfolgen. Wenn Sie Chrome mit dem Befehlszeilen-Flag --enable-blink-features='ForceEagerMeasureMemory' starten, wird das Zeitlimit auf null reduziert und ist für die lokale Fehlerbehebung und Tests nützlich.

Beispiel

Die empfohlene Verwendung der API besteht darin, einen globalen Arbeitsspeichermonitor zu definieren, der die Arbeitsspeichernutzung der gesamten Webseite analysiert und die Ergebnisse zur Aggregation und Analyse an einen Server sendet. Die einfachste Methode ist die regelmäßige Stichprobenerhebung, z. B. alle M Minuten. Dies führt jedoch zu Verzerrungen der Daten, da zwischen den Stichproben Speicherspitzen auftreten können.

Das folgende Beispiel zeigt, wie unverzerrte Arbeitsspeichermessungen mit einem Poisson-Prozess durchgeführt werden, der garantiert, dass zu jedem Zeitpunkt Stichproben mit gleicher Wahrscheinlichkeit auftreten (Demo, Quelle).

Definieren Sie zuerst eine Funktion, die die nächste Arbeitsspeichermessung mithilfe von setTimeout() mit 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 im Durchschnitt alle fünf Minuten eine Messung stattfindet. Weitere Informationen finden Sie unter Exponentielle 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 performMeasurement()-Funktion die API auf, zeichnet das Ergebnis auf 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 mit der Messung.

// Start measurements.
scheduleMeasurement();

Das Ergebnis könnte 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 gesamten Arbeitsspeichernutzung wird im Feld bytes zurückgegeben. Dieser Wert ist stark implementierungsabhängig und kann nicht browserübergreifend verglichen werden. Er kann sogar zwischen verschiedenen Versionen desselben Browsers wechseln. Der Wert umfasst JavaScript- und DOM-Speicher aller iFrames, zugehörigen 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 weist ihn einer Reihe von Fenstern, iFrames und Workern zu, die durch URL identifiziert werden. Im Feld types werden die implementierungsspezifischen Speichertypen aufgeführt, die dem Arbeitsspeicher zugeordnet sind.

Es ist wichtig, alle Listen generisch zu behandeln und keine Annahmen hartcodieren, die auf einem bestimmten Browser basieren. Einige Browser geben beispielsweise ein leeres breakdown oder ein leeres attribution zurück. Andere Browser geben möglicherweise mehrere Einträge in attribution zurück, was darauf hinweist, dass sie nicht unterscheiden konnten, welcher dieser Einträge Eigentümer des Arbeitsspeichers ist.

Feedback

Die Web Performance Community Group und das Chrome-Team würden gerne Ihre Meinung zu performance.measureUserAgentSpecificMemory() hören.

Informationen zum API-Design

Gibt es etwas an der API, das nicht wie erwartet funktioniert? Oder fehlen Eigenschaften, die Sie für die Umsetzung Ihrer Idee benötigen? Sie können ein Spezifikationsproblem im GitHub-Repository für Performance.measureUserAgentSpecificMemory() melden oder Ihre Gedanken zu einem bereits vorhandenen Problem hinzufügen.

Problem mit der Implementierung melden

Haben Sie einen Fehler bei der Implementierung in Chrome gefunden? Oder unterscheidet sich die Implementierung von der Spezifikation? Melde einen Fehler unter new.crbug.com. Gib so viele Details wie möglich an, stelle eine einfache Anleitung zum Reproduzieren des Fehlers bereit und setze Components auf Blink>PerformanceAPIs. Glitch eignet sich perfekt, um schnelle und einfache Reproduzierungen zu teilen.

Support zeigen

Möchten Sie performance.measureUserAgentSpecificMemory() nutzen? Ihre öffentliche Unterstützung hilft dem Chrome-Team dabei, 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 es verwenden.

Nützliche Links

Danksagungen

Vielen Dank an Domenic Denicola, Yoav Weiss, Mathias Bynens für API-Design-Reviews und Dominik Inführ, Hannes Payer, Kentaro Hara und Michael Lippautz für Code-Rezensionen in Chrome. Ich danke auch Per Parker, Philipp Weis, Olga Belomestnykh, Matthew Bolohan und Neil Mckay für das wertvolle Nutzerfeedback, das die API erheblich verbessert hat.

Hero-Image von Harrison Broadbent auf Unsplash