Hier erfahren Sie, wie Sie die Speichernutzung Ihrer Webseite in der Produktion messen können, um Regressionen zu erkennen.
Browser verwalten den Speicher von Webseiten automatisch. Immer wenn auf einer Webseite ein Objekt erstellt wird, weist der Browser im Hintergrund einen Speicherbereich zum Speichern des Objekts zu. Da 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 um den zugrunde liegenden Arbeitsspeicherblock freizugeben.
Die Erkennung ist jedoch nicht perfekt und es wurde nachgewiesen, dass eine perfekte Erkennung unmöglich ist. Daher nähern Browser die Vorstellung von „ein Objekt ist erforderlich“ mit der Vorstellung von „ein Objekt ist erreichbar“ an. Wenn die Webseite über ihre Variablen und die Felder anderer erreichbarer Objekte nicht auf ein Objekt zugreifen kann, kann der Browser das Objekt sicher zurückfordern. Der Unterschied zwischen diesen beiden Begriffen 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);
Das größere Array b
wird hier nicht mehr benötigt, aber der Browser gibt es nicht frei, da es im Callback weiterhin über object.b
erreichbar ist. Dadurch geht der Speicher des größeren Arrays verloren.
Speicherlecks sind im Web weit verbreitet. Sie können ganz einfach eingeführt werden, z. B. wenn Sie vergessen, einen Event-Listener zu deregistrieren, wenn Sie versehentlich Objekte aus einem iFrame erfassen, wenn Sie einen Worker nicht schließen oder wenn sich Objekte in Arrays ansammeln. Wenn eine Webseite Speicherlecks aufweist, steigt die Speichernutzung im Laufe der Zeit und die Webseite wirkt für die Nutzer langsam und aufgebläht.
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 Produktion messen und so Speicherlecks erkennen, die bei lokalen Tests nicht auffallen.
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 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 Speicher schätzt. Dieser Unterschied ist wichtig, wenn Chrome denselben Heap für mehrere Webseiten (oder mehrere Instanzen derselben Webseite) verwendet. 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 aussichtslos.
Ein weiterer Unterschied besteht darin, dass die neue API die Speichernutzung während der Garbage Collection misst. Dadurch wird das Rauschen in den Ergebnissen reduziert, es kann aber eine Weile dauern, bis die Ergebnisse generiert werden. Andere Browser können die neue API auch ohne Garbage Collection implementieren.
Empfohlene Anwendungsfälle
Die Speichernutzung einer Webseite hängt vom Zeitpunkt von Ereignissen, Nutzeraktionen und Garbage Collections ab. Daher ist die API zur Messung des Arbeitsspeichers für die Aggregation von Daten zur Arbeitsspeichernutzung aus der Produktion vorgesehen. Die Ergebnisse einzelner Anrufe 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 neue Funktionen durchführen, um die Auswirkungen auf den Arbeitsspeicher zu bewerten und Speicherlecks zu erkennen.
- Die Speichernutzung mit der Sitzungsdauer korrelieren, um das Vorhandensein oder Fehlen von Speicherlecks zu überprüfen.
- Korrelation der Speichernutzung mit Nutzermesswerten, um die Gesamtauswirkungen der Speichernutzung zu verstehen.
Browserkompatibilität
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 schließen möglicherweise einige Speicherbereiche aus der Berechnung aus, wenn eine korrekte Berechnung zu aufwendig oder nicht durchführbar 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 einem SecurityError fehl, wenn die Ausführungsumgebung die Sicherheitsanforderungen zur Verhinderung von Informationslecks zwischen verschiedenen Quellen nicht erfüllt.
Sie basiert auf der Cross-Origin-Isolierung, 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 das Ergebnis-Promise der API nicht sofort aufgelöst wird, sondern auf die nächste automatische Speicherbereinigung gewartet wird.
Durch den Aufruf der API wird nach einem bestimmten Zeitlimit, das derzeit auf 20 Sekunden festgelegt ist, eine Garbage Collection erzwungen. Dies kann jedoch auch früher geschehen. Wenn Sie Chrome mit dem Befehlszeilen-Flag --enable-blink-features='ForceEagerMeasureMemory'
starten, wird das Zeitlimit 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 Stichprobenerhebung, z. B. alle M
Minuten. Dadurch werden die Daten jedoch verzerrt, da es zwischen den Stichproben zu Speicher-Peaks kommen kann.
Im folgenden Beispiel wird gezeigt, wie Sie mit einem Poisson-Prozess unvoreingenommene Messungen des Arbeitsspeichers durchführen. Dadurch wird sichergestellt, dass Stichproben zu jedem Zeitpunkt gleich wahrscheinlich sind (Demo, Quelle).
Definieren Sie zuerst eine Funktion, die die nächste Speichermessung mit einem zufälligen Intervall mithilfe von setTimeout()
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 erfolgt. Wenn Sie sich für die mathematischen Grundlagen der Funktion interessieren, lesen Sie den Artikel Exponentialverteilung.
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, 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 schließlich 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 Gesamtspeichernutzung wird im Feld bytes
zurückgegeben. Dieser Wert ist stark implementierungsabhängig und kann nicht browserübergreifend verglichen werden. Sie kann sich sogar zwischen verschiedenen Versionen desselben Browsers ändern. Der Wert umfasst den JavaScript- und DOM-Arbeitsspeicher aller iFrames, zugehörigen Fenster und Webworkern im aktuellen Prozess.
Die Liste breakdown
enthält weitere Informationen zum verwendeten Arbeitsspeicher. Jeder Eintrag beschreibt einen Teil des Speichers und ordnet ihn einer Reihe von Fenstern, iFrames und Workern zu, die durch die URL identifiziert werden. Im Feld types
werden die implementationsspezifischen Speichertypen aufgeführt, die mit dem Speicher verknüpft sind.
Es ist wichtig, alle Listen generisch zu behandeln und keine Annahmen auf Grundlage eines bestimmten Browsers fest zu codieren. In einigen Browsern wird beispielsweise ein leerer breakdown
- oder attribution
-Wert zurückgegeben. 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 Speicher belegt.
Feedback
Die Web Performance Community Group und das Chrome-Team würden sich freuen, mehr über Ihre Gedanken und Erfahrungen mit performance.measureUserAgentSpecificMemory()
zu erfahren.
Informationen zum API-Design
Gibt es etwas an der API, das nicht wie erwartet funktioniert? Oder fehlen Properties, die Sie für die Umsetzung Ihrer Idee benötigen? Melden Sie ein Spezifikationsproblem im GitHub-Repository für performance.measureUserAgentSpecificMemory() oder fügen Sie Ihre Gedanken zu einem bestehenden 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 zum Reproduzieren des Fehlers bereit und legen Sie Components auf 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 Funktion verwenden.
Nützliche Links
- Erläuterung
- Demo | Demoquelle
- Tracking-Fehler
- ChromeStatus.com-Eintrag
- Änderungen seit der Origin Trial API
- Abgeschlossener Origin-Test
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-Reviews in Chrome. Außerdem möchte ich mich bei Per Parker, Philipp Weis, Olga Belomestnykh, Matthew Bolohan und Neil Mckay für das wertvolle Nutzer-Feedback bedanken, das die API erheblich verbessert hat.
Hero-Bild von Harrison Broadbent auf Unsplash