Gesamtspeichernutzung Ihrer Webseite mit measureUserAgentSpecificMemory() überwachen

Hier erfahren Sie, wie Sie die Speichernutzung 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. Wenn auf einer Webseite ein Objekt erstellt wird, weist der Browser „intern“ einen Speicherblock zu, um das Objekt zu speichern. Da der Arbeitsspeicher eine endliche Ressource ist, führt der Browser eine Garbage Collection durch, um zu erkennen, wann ein Objekt nicht mehr benötigt wird, und den zugrunde liegenden Speicherblock freizugeben.

Die Erkennung ist jedoch nicht perfekt und es wurde nachgewiesen, dass eine perfekte Erkennung unmöglich ist. Daher gleichsetzen Browser den Begriff „ein Objekt ist erforderlich“ mit dem Begriff „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 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 ist das größere Array b nicht mehr erforderlich, wird aber vom Browser nicht zurückgefordert, da es im Callback weiterhin über object.b erreichbar ist. Daher wird der Speicher des größeren Arrays geleert.

Speicherlecks sind im Web weit verbreitet. Es ist leicht, eine solche Situation herbeizuführen, indem Sie beispielsweise 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 Speichernutzung im Laufe der Zeit an und die Webseite erscheint den Nutzern langsam und überladen.

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

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

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

Ein weiterer Unterschied besteht darin, dass die neue API während der automatischen Speicherbereinigung Arbeitsspeichermessungen durchführt. Dadurch werden die Ergebnisse genauer. Es kann jedoch etwas dauern, bis sie verfügbar sind. Andere Browser entscheiden sich möglicherweise dafür, die neue API zu implementieren, ohne die automatische Speicherbereinigung einzusetzen.

Empfohlene Anwendungsfälle

Die Arbeitsspeichernutzung einer Webseite hängt vom Timing von Ereignissen, Nutzeraktionen und Garbage Collection ab. Aus diesem Grund ist die API zur Arbeitsspeichermessung für die Zusammenführung von Daten zur Arbeitsspeichernutzung aus der Produktionsumgebung vorgesehen. Die Ergebnisse einzelner Aufrufe sind weniger nützlich. Beispiele für Anwendungsfälle:

  • Regressionserkennung beim Roll-out 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.
  • Speichernutzung mit Sitzungsdauer in Beziehung setzen, um festzustellen, ob es Speicherlecks gibt oder nicht
  • Speichernutzung mit Nutzermesswerten in Beziehung setzen, um die Gesamtauswirkung der Speichernutzung zu verstehen.

Browserkompatibilität

Unterstützte Browser

  • Chrome: 89.
  • Edge: 89.
  • Firefox: Nicht unterstützt.
  • Safari: wird nicht unterstützt.

Quelle

Derzeit wird die API nur in Chromium-basierten Browsern unterstützt, ab Chrome 89. 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 Speicherbereiche von der Erfassung ausschließen, wenn eine ordnungsgemäße Erfassung zu teuer oder nicht möglich ist. Daher können die Ergebnisse nicht browserübergreifend verglichen werden. Es ist nur sinnvoll, die Ergebnisse für denselben Browser zu vergleichen.

performance.measureUserAgentSpecificMemory() verwenden

Funktionserkennung

Die Funktion performance.measureUserAgentSpecificMemory ist nicht verfügbar oder schlägt mit SecurityError fehl, wenn die Ausführungsumgebung nicht die Sicherheitsanforderungen erfüllt, um Datenlecks zwischen verschiedenen Ursprüngen zu verhindern. Sie basiert auf der Isolierung von Ursprungswebsites, die auf einer Webseite durch Festlegen von COOP+COEP-Headern aktiviert werden kann.

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 Garbage Collection durch. Das bedeutet, dass die API das Ergebnisversprechen nicht sofort auflöst, sondern stattdessen auf die nächste Garbage Collection 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 die Zeitüberschreitung auf null gesetzt. Das ist nützlich für die lokale Fehlerbehebung und -prüfung.

Beispiel

Die empfohlene Verwendung der API ist die 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 mit einem Poisson-Prozess unvoreingenommene Speichermessungen durchführen, was dafür sorgt, dass zu jedem Zeitpunkt mit gleicher Wahrscheinlichkeit Stichproben auftreten (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 measurementInterval()-Funktion berechnet ein zufälliges Intervall in Millisekunden, sodass durchschnittlich alle fünf Minuten eine Messung erfolgt. Unter Exponentialverteilung finden Sie Informationen zur Mathematik hinter der Funktion.

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 ist stark von der Implementierung abhängig und kann nicht zwischen Browsern verglichen werden. Es kann sich sogar zwischen verschiedenen Versionen desselben Browsers ändern. Der Wert umfasst den JavaScript- und DOM-Speicher aller Iframes, zugehörigen Fenster und Webworker 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 eine URL identifiziert werden. Im Feld types sind die implementierungsspezifischen Speichertypen aufgeführt, die mit dem Speicher verknüpft 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 eine leere breakdown oder eine leere attribution zurück. Andere Browser geben möglicherweise mehrere Einträge in attribution zurück, was darauf hinweist, dass nicht unterschieden werden konnte, welcher dieser Einträge der Eigentümer des Arbeitsspeichers ist.

Feedback

Die Web Performance Community Group und das Chrome-Team würden sich sehr über Ihre Meinung und Ihre Erfahrungen mit performance.measureUserAgentSpecificMemory() freuen.

Informationen zum API-Design

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

Problem mit der Implementierung melden

Haben Sie einen Fehler in der Chrome-Implementierung 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 schnell und einfach Reproduktionen zu teilen.

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 die Unterstützung dieser Funktionen ist. Senden 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 möchte ich Per Parker, Philipp Weis, Olga Belomestnykh, Matthew Bolohan und Neil Mckay für ihr wertvolles Nutzerfeedback danken, das die API erheblich verbessert hat.

Hero-Image von Harrison Broadbent auf Unsplash