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
Ulan Degenbaev
Ulan Degenbaev

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

Die Erkennung ist jedoch nicht perfekt, und es hat sich erwiesen, dass eine perfekte Erkennung unmöglich ist. Daher ähneln sich Browser dem Konzept "ein Objekt ist erforderlich" mit dem Konzept "ein Objekt ist erreichbar". Wenn die Webseite ein Objekt über seine 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, wie im folgenden Beispiel veranschaulicht, zu Speicherlecks.

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 nimmt es nicht zurück, da es weiterhin über object.b im Callback erreichbar ist. Daher kommt es zu einem Speicherleck des größeren Arrays.

Im Web treten häufig Speicherlecks auf. Sie können ganz einfach einen Ereignis-Listener einführen, indem Sie vergessen, die Registrierung eines Ereignis-Listeners aufzuheben, indem Sie versehentlich Objekte aus einem iFrame erfassen, einen Worker nicht schließen, Objekte in Arrays akkumulieren usw. Wenn auf einer Webseite Speicherlecks auftreten, wächst die Arbeitsspeichernutzung mit der Zeit und die Webseite wirkt langsam und aufgebläht für die Nutzer.

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

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

Wenn Sie mit der vorhandenen nicht standardmäßigen API performance.memory vertraut sind, fragen Sie sich möglicherweise, inwiefern sich die neue API von dieser 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 wird 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 sein. Da die alte API in implementierungsspezifischen Begriffen wie „Heap“ definiert ist, ist eine Standardisierung hoffnungslos.

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

Empfohlene Anwendungsfälle

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

  • Regressionserkennung während des Roll-outs einer neuen Version der Webseite, um neue Speicherlecks zu erkennen.
  • A/B-Tests einer neuen Funktion, um die Auswirkungen auf den Arbeitsspeicher zu bewerten und Speicherlecks zu erkennen.
  • Korrelieren der Arbeitsspeichernutzung und der Sitzungsdauer, um das Vorhandensein oder Fehlen von Speicherlecks zu prüfen.
  • Arbeitsspeichernutzung mit Nutzermesswerten korrelieren, um die Gesamtauswirkungen der Arbeitsspeichernutzung 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 hängt stark von der Implementierung ab, da Browser Objekte auf unterschiedliche Weise im Arbeitsspeicher darstellen und die Arbeitsspeichernutzung auf unterschiedliche Weise schätzen. Browser können einige Speicherregionen von der Berücksichtigung ausschließen, wenn eine ordnungsgemäße Berücksichtigung zu teuer oder unmöglich ist. Daher können die Ergebnisse nicht browserübergreifend verglichen werden. Es ist nur sinnvoll, die Ergebnisse desselben Browsers zu vergleichen.

performance.measureUserAgentSpecificMemory() verwenden

Funktionserkennung

Die Funktion performance.measureUserAgentSpecificMemory ist nicht verfügbar oder schlägt möglicherweise mit einem SecurityError fehl, wenn die Ausführungsumgebung die Sicherheitsanforderungen zur Vermeidung 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 während der Laufzeit wird erkannt:

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. Dies bedeutet, dass die API das Ergebnisversprechen nicht sofort auflöst und stattdessen auf die nächste automatische Speicherbereinigung wartet.

Der Aufruf der API erzwingt nach einer Zeitüberschreitung, die derzeit auf 20 Sekunden festgelegt ist, eine automatische Speicherbereinigung. Der Vorgang kann aber auch früher erfolgen. Wenn Sie Chrome mit dem Befehlszeilen-Flag --enable-blink-features='ForceEagerMeasureMemory' starten, wird das Zeitlimit auf null reduziert. Dies ist nützlich für lokale Fehlerbehebungen und Tests.

Beispiel

Die empfohlene Verwendung der API besteht in der Definition eines globalen Speichermonitors, der die Arbeitsspeichernutzung der gesamten Webseite abtastet und die Ergebnisse zur Aggregation und Analyse an einen Server sendet. Die einfachste Möglichkeit besteht darin, regelmäßig Stichproben zu nehmen, 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 Sie mithilfe eines Poisson-Prozesses unverzerrte Arbeitsspeichermessungen durchführen, die garantiert, dass Stichproben zu jedem Zeitpunkt gleich wahrscheinlich auftreten (Demo, Quelle).

Definieren Sie zuerst eine Funktion, die die nächste Arbeitsspeichermessung mithilfe von setTimeout() mit einem zufällig ausgewählten 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 stattfindet. Weitere Informationen zur Berechnung der Funktion 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 nun 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 hängt stark von der Implementierung ab und kann nicht browserübergreifend verglichen werden. Er kann sich sogar zwischen verschiedenen Versionen desselben Browsers ändern. Der Wert umfasst den 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 ordnet ihn einer Reihe von Fenstern, iFrames und Workern zu, die durch die URL identifiziert werden. Im Feld types werden die implementierungsspezifischen Speichertypen aufgelistet, die dem Arbeitsspeicher zugeordnet sind.

Es ist wichtig, alle Listen generisch zu behandeln und Annahmen, die auf einem bestimmten Browser basieren, nicht hart zu codieren. 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, die darauf hinweisen, dass sie nicht unterscheiden konnten, welchem dieser Einträge der Speicher gehört.

Feedback

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

Erzähl uns etwas über das 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? Reichen Sie ein Spezifikationsproblem im performance.measureUserAgentSpecificMemory() GitHub-Repository ein oder fügen Sie Ihre Gedanken zu einem vorhandenen Problem hinzu.

Problem mit der Implementierung melden

Haben Sie bei der Implementierung von Chrome einen Fehler gefunden? Oder unterscheidet sich die Implementierung von der Spezifikation? Melden Sie einen Fehler unter new.crbug.com. Geben Sie so viele Details wie möglich an, geben Sie eine einfache Anleitung zum Reproduzieren des Fehlers an und setzen Sie Components auf Blink>PerformanceAPIs. Glitch eignet sich hervorragend, um schnelle und einfache Reproduktionen zu teilen.

Support anzeigen

Möchtest du performance.measureUserAgentSpecificMemory() verwenden? Dein öffentlicher Support hilft dem Chrome-Team dabei, Funktionen zu priorisieren, und zeigt anderen Browseranbietern, wie wichtig der Support für sie ist. Schicken Sie uns 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-Designrezensionen und Dominik Inführ, Hannes Payer, Kentaro Hara und Michael Lippautz für Code Reviews in Chrome. Außerdem danke ich Per Parker, Philipp Weis, Olga Belomestnykh, Matthew Bolohan und Neil Mckay für ihr wertvolles Nutzerfeedback, durch das die API erheblich verbessert wurde.

Hero-Image von Harrison Broadbent auf Unsplash