Monitora l'utilizzo totale della memoria della tua pagina web con MeasurementUserAgentSpecificMemory()

Scopri come misurare l'utilizzo della memoria della tua pagina web in produzione per rilevare regressioni.

Brendan Kenny
Brendan Kenny
Ulan Degenbaev
Ulan Degenbaev

I browser gestiscono automaticamente la memoria delle pagine web. Ogni volta che una pagina web crea un oggetto, il browser alloca un blocco di memoria "under the hood" a archiviare l'oggetto. Poiché la memoria è una risorsa limitata, il browser esegue garbage collection per rilevare quando un oggetto non è più necessario e liberare il blocco di memoria sottostante.

Il rilevamento non è perfetto però e ha dimostrato che il rilevamento perfetto è un compito impossibile. Pertanto, i browser approssimano la nozione di "un oggetto necessaria". con la nozione di "un oggetto è raggiungibile". Se la pagina web non può raggiungere un oggetto tramite le sue variabili e i campi di altri oggetti raggiungibili, il browser può rivendicare l'oggetto in modo sicuro. La differenza tra questi due concetti portano a perdite di memoria, come illustrato nell'esempio seguente.

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

In questo caso l'array più grande b non è più necessario, ma il browser non recuperala perché è ancora raggiungibile tramite object.b nel callback. Pertanto, viene divulgata la memoria dell'array più grande.

Le fughe di memoria sono prevalenti sul web. È facile introdurne uno dimenticando di annullare la registrazione di un listener di eventi, acquisire accidentalmente oggetti da un iframe, non chiudendo un worker, accumulando oggetti in array e così via. Se una pagina web presenta perdite di memoria, l'utilizzo della memoria aumenta nel tempo e la pagina web appare lenta e per gli utenti.

Il primo passo per risolvere questo problema è misurarlo. Il nuovo L'API performance.measureUserAgentSpecificMemory() consente agli sviluppatori misurare l'utilizzo della memoria delle proprie pagine web in produzione e rilevare quindi la memoria perdite che non riescono a superare i test locali.

Qual è la differenza tra performance.measureUserAgentSpecificMemory() e l'API performance.memory precedente?

Se hai familiarità con l'API performance.memory non standard esistente, forse ti stai chiedendo cosa differenzia la nuova API. La differenza principale è che la vecchia API restituisce la dimensione dell'heap JavaScript, mentre la nuova API stima la memoria utilizzata dalla pagina web. Questa differenza diventa è importante quando Chrome condivide lo stesso heap con più pagine web (o più istanze della stessa pagina web). In questi casi, il risultato del vecchio L'API potrebbe essere arbitrariamente disattivata. Poiché la vecchia API è definita termini specifici dell'implementazione come "heap", la standardizzazione è inutile.

Un'altra differenza è che la nuova API esegue la misurazione della memoria garbage collection. In questo modo si riduce il rumore nei risultati, ma potrebbe essere necessario un finché non vengono generati i risultati. Tieni presente che altri browser potrebbero decidere di di implementare la nuova API senza fare affidamento sulla garbage collection.

Casi d'uso suggeriti

L'utilizzo della memoria di una pagina web dipende dalla tempistica degli eventi, dalle azioni degli utenti e garbage collection. Ecco perché l'API di misurazione della memoria è pensata aggregando i dati sull'utilizzo della memoria dalla produzione. I risultati delle singole chiamate sono meno utili. Esempi di casi d'uso:

  • Rilevamento della regressione durante l'implementazione di una nuova versione della pagina web per rilevare nuove perdite di memoria.
  • Test A/B di una nuova funzionalità per valutare l'impatto sulla memoria e rilevare le perdite di memoria.
  • Correlazione dell'utilizzo della memoria con la durata della sessione per verificare la presenza o l'assenza di perdite di memoria.
  • Correlazione dell'utilizzo della memoria con le metriche utente per comprendere l'impatto complessivo della memoria utilizzata.

Compatibilità del browser

Supporto dei browser

  • Chrome: 89.
  • Edge: 89.
  • Firefox: non supportato.
  • Safari: non supportato.

Origine

Attualmente l'API è supportata solo nei browser basati su Chromium, a partire da Chrome 89. La dipende molto dall'implementazione perché i browser hanno diversi modi di rappresentare gli oggetti in memoria e diversi modi di la stima dell'utilizzo della memoria. I browser possono escludere alcune regioni di memoria da una corretta contabilità se una corretta contabilità è troppo costosa o non fattibile. Di conseguenza, i risultati non possono essere confrontati tra browser. Ha senso solo confrontare per lo stesso browser.

In uso: performance.measureUserAgentSpecificMemory()

Rilevamento delle caratteristiche

La funzione performance.measureUserAgentSpecificMemory non sarà disponibile o potrebbe hanno esito negativo con un messaggio SecurityError se l'ambiente di esecuzione non soddisfa i requisiti di sicurezza per prevenire le fughe di informazioni tra origini. Si basa sull'isolamento multiorigine, che una pagina web può attivare impostando le intestazioni COOP+COEP.

Il supporto può essere rilevato in fase di esecuzione:

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);
}

Test locale

Chrome esegue la misurazione della memoria durante la garbage collection, ovvero che l'API non risolva immediatamente la promessa dei risultati, ma attende per la prossima garbage collection.

La chiamata all'API forza una garbage collection dopo un timeout, ovvero è attualmente impostato su 20 secondi, ma potrebbe avvenire prima. L'avvio di Chrome con --enable-blink-features='ForceEagerMeasureMemory' flag della riga di comando riduce il timeout su zero ed è utile per il debug e i test locali.

Esempio

L'uso consigliato dell'API è definire un monitoraggio della memoria globale che campiona l'utilizzo della memoria dell'intera pagina web e invia i risultati a un server per l'aggregazione e l'analisi. Il modo più semplice è eseguire il campionamento periodicamente, esempio ogni M minuti. Tuttavia, ciò introduce un bias nei dati perché tra i campioni potrebbero verificarsi picchi di memoria.

L'esempio seguente mostra come effettuare misurazioni imparziali della memoria utilizzando il processo di Poisson, che garantisce la stessa probabilità che i campioni si verifichino in qualsiasi momento (demo, fonte).

Innanzitutto, definisci una funzione che pianifichi la successiva misurazione della memoria usando setTimeout() con un intervallo randomizzato.

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);
}

La funzione measurementInterval() calcola un intervallo casuale in millisecondi in modo che in media venga eseguita una misurazione ogni cinque minuti. Vedi Esponenziale distribuzione se ti interessa conoscere i calcoli matematici alla base della funzione.

function measurementInterval() {
  const MEAN_INTERVAL_IN_MS = 5 * 60 * 1000;
  return -Math.log(Math.random()) * MEAN_INTERVAL_IN_MS;
}

Infine, la funzione performMeasurement() asincrona richiama l'API, registra il risultato e pianifica la misurazione successiva.

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();
}

Infine, inizia la misurazione.

// Start measurements.
scheduleMeasurement();

Il risultato potrebbe avere il seguente aspetto:

// 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']
    },
  ],
}

La stima dell'utilizzo totale della memoria viene restituita nel campo bytes. Questo valore è dipende fortemente dall'implementazione e non può essere confrontata tra browser. Potrebbe passare da una versione all'altra dello stesso browser. Il valore include Memoria JavaScript e DOM di tutti gli iframe, finestre correlate e web worker in il processo attuale.

L'elenco breakdown fornisce ulteriori informazioni sulla memoria utilizzata. Ciascuna descrive una parte della memoria e la attribuisce a un insieme finestre, iframe e worker identificati dall'URL. Il campo types elenca i tipi di memoria specifici dell'implementazione associati alla memoria.

È importante trattare tutti gli elenchi in modo generico e non applicare una codifica rigida ipotesi basate su un determinato browser. Ad esempio, alcuni browser restituisce un valore breakdown vuoto o un valore attribution vuoto. Altri browser potrebbero restituisce più voci in attribution che indicano che non è possibile distinguere quale di queste voci possiede la memoria.

Feedback

Il Web Performance Community Group e il team di Chrome vorrebbero di conoscere le tue opinioni ed esperienze con performance.measureUserAgentSpecificMemory().

Parlaci della progettazione dell'API

C'è qualcosa che non funziona come previsto nell'API? Oppure ci sono mancano delle proprietà necessarie per implementare la tua idea? Segnala un problema con le specifiche il repository GitHub performance.measureUserAgentSpecificMemory() o aggiungi le tue opinioni su un problema esistente.

Segnalare un problema con l'implementazione

Hai trovato un bug nell'implementazione di Chrome? Oppure l'implementazione rispetto alle specifiche? Segnala un bug all'indirizzo new.crbug.com. Assicurati di includere il maggior numero di dettagli possibile, fornire semplici istruzioni per la riproduzione il bug e impostare Componenti su Blink>PerformanceAPIs. Glitch è la soluzione perfetta per condividere riproduzioni in modo facile e veloce.

Mostra il tuo sostegno

Hai intenzione di utilizzare performance.measureUserAgentSpecificMemory()? Il tuo supporto pubblico aiuta il team di Chrome a dare la priorità alle funzionalità e mostra ad altri fornitori di browser come è fondamentale supportarli. Invia un tweet a @ChromiumDev e facci sapere dove e come lo utilizzi.

Link utili

Ringraziamenti

Ringraziamo Domenic Denicola, Yoav Weiss e Mathias Bynens per le recensioni sulla progettazione delle API. e Dominik Inführ, Hannes Payer, Kentaro Hara, Michael Lippautz per le revisioni del codice. in Chrome. Ringrazio anche Per Parker, Philipp Weis, Olga Belomestnykh, Matthew Bolohan e Neil Mckay per aver fornito un prezioso feedback agli utenti che ha notevolmente ha migliorato l'API.

Immagine hero di Harrison Broadbent su Unsplash