Hier erfahren Sie, wie Sie die Speichernutzung Ihrer Webseite in der Produktion messen, um Regressionen zu erkennen.
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 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 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-Hoops 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 die Arbeitsspeichermessung während der Garbage Collection durchführt. Dadurch werden die Ergebnisse genauer. Es kann jedoch etwas dauern, bis sie verfügbar sind. Andere Browser können die neue API implementieren, ohne die Garbage Collection zu verwenden.
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-Test 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
Derzeit wird die API nur in Chromium-basierten Browsern unterstützt, ab Chrome 89. Das Ergebnis der API ist stark von der Implementierung abhängig, da Browser Objekte im Arbeitsspeicher unterschiedlich darstellen und die Arbeitsspeichernutzung unterschiedlich 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 plattformü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 dem Fehler 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.
Wenn die API aufgerufen wird, wird nach einem bestimmten Zeitlimit eine Garbage Collection erzwungen. Dieses Zeitlimit ist derzeit auf 20 Sekunden festgelegt, kann aber auch früher eintreten. 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
Wir empfehlen, einen globalen Speichermonitor zu definieren, der die Arbeitsspeichernutzung der gesamten Webseite erfasst und die Ergebnisse zur Aggregation und Analyse an einen Server sendet. Am einfachsten ist es, Stichproben regelmäßig zu erheben, z. B. alle M
Minuten. Dies führt jedoch zu einer Verzerrung der Daten, da zwischen den Samples Speicherspitzen auftreten können.
Das folgende Beispiel zeigt, wie Sie mit einem Poisson-Prozess unvoreingenommene Speichermessungen durchführen. So wird sichergestellt, 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();
}
Starten 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 geschätzte Gesamtspeichernutzung 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 hartcodiert zu haben. 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 den Fehler unter new.crbug.com. Geben Sie dabei so viele Details wie möglich an, machen Sie eine einfache Anleitung zur Reproduktion des Fehlers 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 es ist, sie zu unterstützen. Senden Sie uns einen Tweet an @ChromiumDev und teilen Sie uns mit, wo und wie Sie es verwenden.
Nützliche Links
- Erläuterung
- Demo | Demoquelle
- Tracking-Fehler
- Eintrag in ChromeStatus.com
- Änderungen seit der Ursprungstest-API
- Abgeschlossene Origin-Testversion
Danksagungen
Vielen Dank an Domenic Denicola, Yoav Weiss und Mathias Bynens für die API-Designüberprüfungen sowie an Dominik Inführ, Hannes Payer, Kentaro Hara und Michael Lippautz für die Codeüberprüfungen 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