Fissare i problemi di memoria

Impara come usare Chrome e DevTools per trovare i problemi di memoria che influenzano le prestazioni della pagina, incluse le perdite di memoria, il memory bloat e le frequenti garbage collection.

Sommario #

  • Trova quanta memoria sta usando attualmente la tua pagina con il Task Manager di Chrome.
  • Visualizza l’uso della memoria nel tempo con le registrazioni Timeline.
  • Identifica gli alberi DOM staccati (una causa comune di perdite di memoria) con Heap Snapshots.
  • Trova quando viene allocata nuova memoria nel tuo heap JS con le registrazioni Allocation Timeline.

Panoramica #

Nello spirito del modello di prestazioni RAIL, il fulcro dei vostri sforzi sulle prestazioni dovrebbe essere i vostri utenti.

I problemi di memoria sono importanti perché sono spesso percepibili dagli utenti. Gli utenti possono percepire i problemi di memoria nei seguenti modi:

  • Le prestazioni di una pagina peggiorano progressivamente nel tempo. Questo è probabilmente un sintomo di una perdita di memoria. Una perdita di memoria è quando un bug nella pagina fa sì che la pagina usi progressivamente sempre più memoria nel tempo.
  • Le prestazioni di una pagina sono costantemente negative. Questo è probabilmente un sintomo di memory bloat. Il memory bloat è quando una pagina usa più memoria di quella necessaria per una velocità ottimale della pagina.
  • Le prestazioni di una pagina sono ritardate o sembrano fermarsi frequentemente. Questo è probabilmente un sintomo di frequenti garbage collection. La garbage collection è quando il browser recupera la memoria. Il browser decide quando questo accade. Durante le raccolte, tutta l’esecuzione degli script viene messa in pausa. Quindi se il browser sta raccogliendo molta spazzatura, l’esecuzione dello script verrà messa in pausa molto spesso.

Memory bloat: quanto è “troppo”?

Una perdita di memoria è facile da definire. Se un sito sta usando progressivamente sempre più memoria, allora avete una perdita. Ma il memory bloat è un po’ più difficile da definire. Cosa si qualifica come “usare troppa memoria”?

Non ci sono numeri fissi qui, perché dispositivi e browser diversi hanno capacità diverse. La stessa pagina che funziona bene su uno smartphone di fascia alta potrebbe bloccarsi su uno smartphone di fascia bassa.

La chiave qui è usare il modello RAIL e concentrarsi sui tuoi utenti. Scoprite quali dispositivi sono popolari tra i vostri utenti, e poi testate la vostra pagina su quei dispositivi. Se l’esperienza è costantemente negativa, la pagina potrebbe superare le capacità di memoria di quei dispositivi.

Monitorare l’uso della memoria in tempo reale con il Task Manager di Chrome #

Utilizzare il Task Manager di Chrome come punto di partenza per la vostra indagine sui problemi di memoria. Il Task Manager è un monitor in tempo reale che ti dice quanta memoria una pagina sta attualmente utilizzando.

  1. Premere Shift+Esc o andare nel menu principale di Chrome e selezionare Altri strumenti > Task manager per aprire il Task Manager.

    Apertura del Task Manager

  2. Clicca con il tasto destro del mouse sull’intestazione della tabella del Task Manager e attiva la memoria JavaScript.

    Abilitare la memoria JS

Queste due colonne ti dicono cose diverse su come la tua pagina sta usando la memoria:

  • La colonna Memoria rappresenta la memoria nativa. I nodi DOM sono memorizzati nella memoria nativa. Se questo valore è in aumento, i nodi DOM vengono creati.
  • La colonna Memoria JavaScript rappresenta l’heap JS. Questa colonna contiene due valori. Il valore che ti interessa è il numero live (il numero tra parentesi). Il numero live rappresenta quanta memoria stanno usando gli oggetti raggiungibili sulla vostra pagina. Se questo numero sta aumentando, o vengono creati nuovi oggetti, o gli oggetti esistenti stanno crescendo.

Visualizzare le perdite di memoria con le registrazioni della Timeline #

Puoi anche usare il pannello Timeline come altro punto di partenza nella tua indagine. Il pannello Timeline ti aiuta a visualizzare l’uso della memoria di una pagina nel tempo.

  1. Apri il pannello Timeline su DevTools.
  2. Abilita la casella di controllo Memory.
  3. Fai una registrazione.

Tip: è una buona pratica iniziare e finire la registrazione con una garbage collection forzata. Clicca il pulsante collect garbage (force garbage collection button) durante la registrazione per forzare la garbage collection.

Per dimostrare le registrazioni in memoria Timeline, considerate il codice qui sotto:

var x = ;
function grow() {
for (var i = 0; i < 10000; i++) {
document.body.appendChild(document.createElement('div'));
}
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);

Ogni volta che il pulsante a cui si fa riferimento nel codice viene premuto, diecimila nodi div vengono aggiunti al corpo del documento, e una stringa di un milione di x caratteri viene spinta nell’array x. L’esecuzione di questo codice produce una registrazione Timeline come il seguente screenshot:

esempio di crescita semplice

Prima, una spiegazione dell’interfaccia utente. Il grafico HEAP nel pannello Overview (sotto NET) rappresenta l’heap di JS. Sotto il riquadro Overview c’è il riquadro Counter. Qui si può vedere l’uso della memoria suddiviso per heap JS (lo stesso grafico HEAP nel pannello Overview), documenti, nodi DOM, ascoltatori e memoria GPU. Disabilitare una casella di controllo la nasconde dal grafico.

Ora, un’analisi del codice rispetto allo screenshot. Se guardate il contatore di nodi (il grafico verde) potete vedere che corrisponde in modo pulito al codice. Il conteggio dei nodi aumenta a passi discreti. Si può presumere che ogni aumento nel conteggio dei nodi sia una chiamata a grow(). Il grafico dell’heap di JS (il grafico blu) non è così semplice. In linea con le migliori pratiche, il primo tuffo è in realtà una garbage collection forzata (ottenuta premendo il pulsante collect garbage). Man mano che la registrazione progredisce si può vedere che la dimensione dell’heap JS ha un picco. Questo è naturale e atteso: il codice JavaScript sta creando i nodi DOM ad ogni click del pulsante e sta facendo un sacco di lavoro quando crea la stringa di un milione di caratteri. La cosa chiave qui è il fatto che l’heap di JS finisce più in alto di come è iniziato (l'”inizio” qui è il punto dopo la garbage collection forzata). Nel mondo reale, se vedeste questo schema di aumento della dimensione dell’heap JS o del nodo, significherebbe potenzialmente una perdita di memoria.

Scoprire le perdite di memoria dell’albero DOM distaccato con gli Heap Snapshots #

Un nodo DOM può essere raccolto con la garbage collection solo quando non ci sono riferimenti ad esso dall’albero DOM della pagina o dal codice JavaScript. Si dice che un nodo è “staccato” quando è rimosso dall’albero DOM ma alcuni JavaScript fanno ancora riferimento ad esso. I nodi DOM staccati sono una causa comune di perdite di memoria. Questa sezione insegna come usare l’heap profiler di DevTools per identificare i nodi distaccati.

Qui c’è un semplice esempio di nodi DOM distaccati.

var detachedTree;
function create() {
var ul = document.createElement('ul');
for (var i = 0; i < 10; i++) {
var li = document.createElement('li');
ul.appendChild(li);
}
detachedTree = ul;
}
document.getElementById('create').addEventListener('click', create);

Cliccando il pulsante a cui si fa riferimento nel codice si crea un nodo ul con dieci figli li. Questi nodi sono referenziati dal codice ma non esistono nell’albero DOM, quindi sono distaccati.

Le istantanee dell’heap sono un modo per identificare i nodi distaccati. Come il nome implica, le istantanee dell’heap mostrano come la memoria è distribuita tra gli oggetti JS e i nodi DOM della pagina al momento dell’istantanea.

Per creare un’istantanea, aprite DevTools e andate nel pannello Profiles, selezionate il pulsante radio Take Heap Snapshot e premete il pulsante Take Snapshot.

take heap snapshot

L’istantanea potrebbe richiedere del tempo per essere elaborata e caricata. Una volta finito, selezionalo dal pannello di sinistra (chiamato HEAP SNAPSHOTS).

Digita Detached nella casella di testo Class filter per cercare gli alberi DOM staccati.

filtrare per i nodi staccati

Espandi i carati per investigare un albero staccato.

investigare l'albero staccato

I nodi evidenziati in giallo hanno riferimenti diretti ad essi dal codice JavaScript. I nodi evidenziati in rosso non hanno riferimenti diretti. Sono vivi solo perché fanno parte dell’albero del nodo giallo. In generale, volete concentrarvi sui nodi gialli. Sistemate il vostro codice in modo che il nodo giallo non sia vivo più a lungo del necessario, e liberatevi anche dei nodi rossi che fanno parte dell’albero del nodo giallo.

Cliccate su un nodo giallo per esaminarlo ulteriormente. Nel riquadro Objects si possono vedere più informazioni sul codice che fa riferimento ad esso. Per esempio, nello screenshot qui sotto potete vedere che la variabile detachedTree fa riferimento al nodo. Per risolvere questa particolare perdita di memoria, dovreste studiare il codice che usa detachedTree e assicurarvi che rimuova il suo riferimento al nodo quando non è più necessario.

indagare un nodo giallo

Identificare le perdite di memoria dell’heap JS con le Allocation Timeline #

L’Allocation Timeline è un altro strumento che può aiutarvi a rintracciare le perdite di memoria nel vostro heap JS.

Per dimostrare l’Allocation Timeline considerate il seguente codice:

var x = ;
function grow() {
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);

Ogni volta che il pulsante a cui si fa riferimento nel codice viene premuto, una stringa di un milione di caratteri viene aggiunta all’array x.

Per registrare una Allocation Timeline, aprite DevTools, andate nel pannello Profiles, selezionate il pulsante radio Record Allocation Timeline, premete il pulsante Start, eseguite l’azione che sospettate stia causando il memory leak, e poi premete il pulsante stop recording (stop recording button) quando avete finito.

Durante la registrazione, notate se appaiono delle barre blu sulla Timeline delle allocazioni, come nello screenshot qui sotto.

nuove allocazioni

Queste barre blu rappresentano nuove allocazioni di memoria. Queste nuove allocazioni di memoria sono i vostri candidati per le perdite di memoria. Puoi zoomare su una barra per filtrare il riquadro del costruttore per mostrare solo gli oggetti che sono stati allocati durante il periodo specificato.

zoom della timeline di allocazione

Espandi l’oggetto e clicca sul suo valore per vedere più dettagli su di esso nel riquadro dell’oggetto. Per esempio, nello screenshot qui sotto, visualizzando i dettagli dell’oggetto che è stato appena allocato, sarete in grado di vedere che è stato allocato alla variabile x nell’ambito Window.

dettagli dell'oggetto

Investigare l’allocazione della memoria per funzione #

Utilizzare il tipo Record Allocation Profiler per visualizzare l’allocazione della memoria per funzione JavaScript.

Record Allocation Profiler

  1. Selezionare il pulsante radio Record Allocation Profiler. Se c’è un worker nella pagina, puoi selezionarlo come destinazione del profiling usando il menu a tendina vicino al pulsante Start.
  2. Premi il pulsante Start.
  3. Esegui le azioni sulla pagina che vuoi analizzare.
  4. Premi il pulsante Stop quando hai finito tutte le tue azioni.

DevTools ti mostra una ripartizione dell’allocazione della memoria per funzione. La vista predefinita è Heavy (Bottom Up), che mostra le funzioni che hanno allocato più memoria in alto.

Profilo di allocazione

Spot frequenti garbage collections #

Se la tua pagina sembra andare in pausa frequentemente, allora potresti avere problemi di garbage collection.

Puoi usare il Task Manager di Chrome o le registrazioni di memoria della Timeline per individuare frequenti garbage collections. Nel Task Manager, valori di memoria o di memoria JavaScript che salgono e scendono frequentemente rappresentano frequenti garbage collection. Nelle registrazioni della Timeline, i grafici dell’heap JS o del conteggio dei nodi che aumentano e diminuiscono frequentemente indicano frequenti garbage collections.

Una volta identificato il problema, puoi usare una registrazione della Timeline delle allocazioni per scoprire dove viene allocata la memoria e quali funzioni stanno causando le allocazioni.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *