Registra snapshot dell'heap

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

Scopri come registrare istantanee heap con Memoria > Profili > Istantanea heap e trovare perdite di memoria.

Il profiler dell'heap mostra la distribuzione della memoria in base agli oggetti JavaScript della tua pagina e ai nodi DOM correlati. Utilizzalo per acquisire istantanee dell'heap JS, analizzare i grafici della memoria, confrontare le istantanee e trovare perdite di memoria. Per ulteriori informazioni, consulta Albero di conservazione degli oggetti.

Scatta una foto

Per acquisire un'istantanea dell'heap:

  1. In una pagina di cui vuoi creare il profilo, apri DevTools e vai al riquadro Memoria.
  2. Seleziona il tipo di profilazione Snapshot dell'heap , poi seleziona un'istanza VM JavaScript e fai clic su Acquisisci snapshot.

Un tipo di profilazione selezionato e un'istanza VM JavaScript.

Quando il riquadro Memoria carica e analizza l'istantanea, mostra le dimensioni totali degli oggetti JavaScript raggiungibili sotto il titolo dell'istantanea nella sezione ISTANTEANEE HEAP.

Le dimensioni totali degli oggetti raggiungibili.

Gli snapshot mostrano solo gli oggetti del grafico della memoria che sono raggiungibili dall'oggetto globale. L'acquisizione di uno snapshot inizia sempre con la garbage collection.

.

Uno snapshot dell'heap di oggetti Item sparsi.

Cancellare gli snapshot

Per rimuovere tutte le istantanee, fai clic su Cancella tutti i profili:

Cancella tutti i profili.

Visualizza snapshot

Per esaminare gli istantanei da prospettive diverse e per scopi diversi, seleziona una delle visualizzazioni dal menu a discesa in alto:

Visualizza Contenuti Finalità
Riepilogo Oggetti raggruppati per nomi dei costruttori. Utilizzalo per individuare gli oggetti e il loro utilizzo della memoria in base al tipo. Utile per monitorare le fughe di dati DOM.
Confronto Differenze tra due istantanee. Utilizzala per confrontare due (o più) snapshot prima e dopo un'operazione. Verifica la presenza e la causa di una perdita di memoria controllando il delta nella memoria liberata e nel conteggio dei riferimenti.
Contenimento Contenuti dell'heap Fornisce una visione migliore della struttura degli oggetti e aiuta ad analizzare gli oggetti a cui viene fatto riferimento nello spazio dei nomi globale (window) per trovare ciò che li mantiene in vita. Utilizzala per analizzare le chiusure e immergerti negli oggetti a basso livello.
Statistiche Grafico a torta dell'allocazione della memoria Osserva le dimensioni reali delle parti di memoria allocate a codice, stringhe, array JS, array digitati e oggetti di sistema.

La visualizzazione Riepilogo selezionata dal menu a discesa in alto.

Visualizzazione riepilogo

Inizialmente, nella visualizzazione Riepilogo si apre uno snapshot dell'heap che elenca i Costruttori in una colonna. Puoi espandere i costruttori per visualizzare gli oggetti che hanno creato.

La visualizzazione Riepilogo con un costruttore espanso.

Per filtrare i costruttori irrilevanti, digita un nome da ispezionare in Filtro classi nella parte superiore della vista Riepilogo.

I numeri accanto ai nomi dei costruttori indicano il numero totale di oggetti creati con il costruttore. La visualizzazione Riepilogo mostra anche le seguenti colonne:

  • Distanza mostra la distanza dalla radice utilizzando il percorso semplice più breve dei nodi.
  • Dimensioni superficiali mostra la somma delle dimensioni di tutti gli oggetti creati da un determinato costruttore. Le dimensioni ridotte corrispondono alle dimensioni della memoria occupata dall'oggetto stesso. In genere, gli array e le stringhe hanno dimensioni più grandi. Vedi anche Dimensioni degli oggetti.
  • Dimensioni conservate mostra le dimensioni massime conservate nello stesso insieme di oggetti. La dimensione trattenuta indica la quantità di memoria che puoi liberare eliminando un oggetto e rendendo non più raggiungibili i relativi elementi dipendenti. Vedi anche Dimensioni degli oggetti.

Quando espandi un costruttore, la visualizzazione Riepilogo mostra tutte le sue istanze. Ogni istanza riceve un'analisi delle sue dimensioni superficiali e conservate nelle colonne corrispondenti. Il numero dopo il carattere @ è l'ID univoco dell'oggetto. Ti consente di confrontare gli snapshot dell'heap in base all'oggetto.

Filtri del costruttore

La visualizzazione Riepilogo ti consente di filtrare i costruttori in base a casi comuni di utilizzo inefficiente della memoria.

Per utilizzare questi filtri, seleziona una delle seguenti opzioni dal menu a discesa più a destra nella barra delle azioni:

  • Tutti gli oggetti: tutti gli oggetti acquisiti dallo snapshot corrente. Impostato per impostazione predefinita.
  • Oggetti allocati prima dello snapshot 1: gli oggetti creati e rimasti in memoria prima del primo snapshot.
  • Oggetti allocati tra gli istantanei 1 e 2: mostra la differenza tra gli oggetti dell'istantanea più recente e quella precedente. Ogni nuovo snapshot aggiunge un incremento di questo filtro all'elenco a discesa.
  • Stringhe duplicate: valori di stringa che sono stati archiviati più volte in memoria.
  • Oggetti conservati da nodi scollegati: oggetti che vengono mantenuti attivi perché un nodo DOM scollegato vi fa riferimento.
  • Oggetti conservati dalla console DevTools: oggetti memorizzati in memoria perché sono stati valutati o con cui è stata eseguita un'interazione tramite la console DevTools.

Voci speciali in Riepilogo

Oltre a raggruppare gli oggetti per costruttore, la visualizzazione Riepilogo raggruppa gli oggetti anche in base a:

  • Funzioni integrate come Array o Object.
  • Elementi HTML raggruppati per tag, ad esempio <div>, <a>, <img> e altri.
  • Funzioni che hai definito nel codice.
  • Categorie speciali non basate su costruttori.

Voci del costruttore.

(array)

Questa categoria include vari oggetti interni simili ad array che non corrispondono direttamente agli oggetti visibili in JavaScript.

Ad esempio, i contenuti degli oggetti Array di JavaScript vengono archiviati in un oggetto interno secondario denominato (object elements)[] per consentire di ridimensionarli più facilmente. Analogamente, le proprietà denominate negli oggetti JavaScript vengono spesso archiviate in oggetti interni secondari denominati (object properties)[], che sono elencati anche nella categoria (array).

(compiled code)

Questa categoria include i dati interni di cui V8 ha bisogno per eseguire funzioni definite da JavaScript o WebAssembly. Ogni funzione può essere rappresentata in vari modi, da piccole e lente a grandi e veloci.

V8 gestisce automaticamente l'utilizzo della memoria in questa categoria. Se una funzione viene eseguita molte volte, V8 utilizza più memoria per quella funzione in modo che possa essere eseguita più velocemente. Se una funzione non viene eseguita da un po' di tempo, V8 potrebbe cancellare i dati interni relativi a quella funzione.

(concatenated string)

Quando V8 concatena due stringhe, ad esempio con l'operatore + di JavaScript, può scegliere di rappresentare il risultato internamente come "stringa concatenata", nota anche come struttura di dati Rope.

Anziché copiare tutti i caratteri delle due stringhe di origine in una nuova stringa, V8 assegna un piccolo oggetto con campi interni chiamati first e second, che puntano alle due stringhe di origine. In questo modo, V8 risparmia tempo e memoria. Dal punto di vista del codice JavaScript, si tratta di normali stringhe che si comportano come qualsiasi altra stringa.

InternalNode

Questa categoria rappresenta gli oggetti allocati al di fuori di V8, ad esempio gli oggetti C++ definiti da Blink.

Per visualizzare i nomi delle classi C++, utilizza Chrome per i test e svolgi i seguenti passaggi:

  1. Apri DevTools e attiva Impostazioni > Esperimenti > Mostra opzione per esporre gli elementi interni negli snapshot dell'heap.
  2. Apri il riquadro Memoria, seleziona Istantanea dell'heap e attiva Esponi dati interni (include dettagli aggiuntivi specifici per l'implementazione).
  3. Riproduci il problema per cui InternalNode ha conservato molta memoria.
  4. Acquisisci un'istantanea dell'heap. In questo snapshot, gli oggetti hanno nomi di classi C++ anziché InternalNode.
(object shape)

Come descritto in Proprietà rapide in V8, V8 tiene traccia delle classi nascoste (o forme) in modo che più oggetti con le stesse proprietà nello stesso ordine possano essere rappresentati in modo efficiente. Questa categoria contiene le classi nascoste, chiamate system / Map (non correlate a Map di JavaScript) e i dati correlati.

(sliced string)

Quando V8 deve prendere una sottostringa, ad esempio quando il codice JavaScript chiama String.prototype.substring(), V8 può scegliere di allocare un oggetto stringa segmentata anziché copiare tutti i caratteri pertinenti dalla stringa originale. Questo nuovo oggetto contiene un puntatore alla stringa originale e descrive l'intervallo di caratteri della stringa originale da utilizzare.

Dal punto di vista del codice JavaScript, si tratta di normali stringhe che si comportano come qualsiasi altra stringa. Se una stringa suddivisa occupa molta memoria, il programma potrebbe aver attivato il problema 2869 e potrebbe trarre vantaggio dall'adozione di misure deliberate per "appiattire" la stringa suddivisa.

system / Context

Gli oggetti interni di tipo system / Context contengono variabili locali di una chiusura, ovvero un ambito JavaScript a cui può accedere una funzione nidificata.

Ogni istanza di funzione contiene un puntatore interno al Context in cui viene eseguita, in modo da poter accedere a queste variabili. Anche se gli oggetti Context non sono direttamente visibili da JavaScript, hai il controllo diretto su di essi.

(system)

Questa categoria contiene vari oggetti interni che non sono (ancora) stati classificati in modo più significativo.

Vista di confronto

La vista Confronto ti consente di trovare gli oggetti divulgati confrontando più snapshot tra loro. Ad esempio, eseguire un'azione e invertirla, come aprire e chiudere un documento, non dovrebbe lasciare oggetti aggiuntivi.

Per verificare che una determinata operazione non crei perdite:

  1. Crea uno snapshot dell'heap prima di eseguire un'operazione.
  2. Esegui un'operazione. In altre parole, interagisci con una pagina in un modo che ritieni possa causare una fuga di dati.
  3. Esegui un'operazione inversa. Vale a dire che devi fare l'interazione opposta e ripeterla alcune volte.
  4. Acquisisci un secondo snapshot dell'heap e modificane la visualizzazione in Confronto, confrontandolo con Snapshot 1.

La vista Confronto mostra la differenza tra due istantanee. Quando espandi una voce di totale, vengono mostrate le istanze di oggetti aggiunte ed eliminate:

Confronto con Snapshot 1.

Visualizzazione Contenimento

La visualizzazione Contenimento è una "panoramica" della struttura degli oggetti dell'applicazione. Ti consente di dare un'occhiata all'interno delle chiusure di funzione, di osservare gli oggetti interni della VM che insieme costituiscono gli oggetti JavaScript e di capire quanta memoria viene utilizzata dalla tua applicazione a un livello molto basso.

La visualizzazione fornisce diversi punti di ingresso:

  • Oggetti DOMWindow. Oggetti globali per il codice JavaScript.
  • Radici GC. Radici GC utilizzate dal garbage collector della VM. Le radici del GC possono essere costituite da mappe di oggetti integrate, tabelle di simboli, stack di thread VM, cache di compilazione, ambiti di handle e handle globali.
  • Oggetti nativi. Oggetti del browser "inviati" all'interno della macchina virtuale JavaScript per consentire l'automazione, ad esempio nodi DOM e regole CSS.

La visualizzazione Contenimento.

Sezione Retainer

La sezione Contenitori nella parte inferiore del riquadro Memoria mostra gli oggetti che rimandano all'oggetto selezionato nella visualizzazione. Il riquadro Memoria aggiorna la sezione Prenotazioni quando selezioni un altro oggetto in una delle visualizzazioni, ad eccezione di Statistiche.

La sezione Apparecchi di contenzione.

In questo esempio, la stringa selezionata viene conservata dalla proprietà x di un'istanza Item.

Ignorare gli elementi di conservazione

Puoi nascondere i fermi per scoprire se altri oggetti trattengono quello selezionato. Con questa opzione, non è necessario rimuovere prima questo elemento di conservazione dal codice e poi acquisire nuovamente lo snapshot dell'heap.

L&#39;opzione &quot;Ignora questo fermo&quot; nel menu a discesa.

Per nascondere un elemento di conservazione, fai clic con il tasto destro del mouse e seleziona Ignora questo elemento di conservazione. I retainer ignorati sono contrassegnati come ignored nella colonna Distanza. Per smettere di ignorare tutti i retainer, fai clic su Ripristina retainer ignorati nella barra delle azioni in alto.

Trovare un oggetto specifico

Per trovare un oggetto nell'heap raccolto, puoi eseguire una ricerca utilizzando Ctrl + F e inserire l'ID oggetto.

Assegna un nome alle funzioni per distinguere le chiusure

È molto utile assegnare un nome alle funzioni per distinguere le chiusure nello snapshot.

Ad esempio, il seguente codice non utilizza funzioni con nome:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function() { // this is NOT a named function
    return largeStr;
  };

  return lC;
}

mentre questo esempio:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function lC() { // this IS a named function
    return largeStr;
  };

  return lC;
}

Funzione con nome in una chiusura.

Scoprire perdite DOM

Il profiler heap è in grado di riflettere le dipendenze bidirezionali tra oggetti nativi del browser (nodi DOM e regole CSS) e oggetti JavaScript. In questo modo è possibile scoprire perdite altrimenti invisibili dovute a sottoalberi DOM scollegati dimenticati.

Le fughe di dati nel DOM possono essere più grandi di quanto pensi. Considera l'esempio seguente. Quando viene raccolta la spazzatura #tree?

  var select = document.querySelector;
  var treeRef = select("#tree");
  var leafRef = select("#leaf");
  var body = select("body");

  body.removeChild(treeRef);

  //#tree can't be GC yet due to treeRef
  treeRef = null;

  //#tree can't be GC yet due to indirect
  //reference from leafRef

  leafRef = null;
  //#NOW #tree can be garbage collected

#leaf mantiene un riferimento al relativo elemento principale (parentNode) e in modo ricorsivo fino a #tree, pertanto solo quando leafRef viene annullato l'intero albero sotto #tree è un candidato per la GC.

Sottoalberi DOM