12 novembre 2026
Trovare memory leak in frontend (davvero): individuare i “detached DOM nodes” con Chrome DevTools e agent
Quando l’app parte fluida e dopo qualche minuto diventa lenta, spesso non è “solo performance”: è memoria che non viene mai liberata.
I memory leak nelle web app moderne sono spesso causati da nodi DOM rimossi dalla pagina ma ancora referenziati da listener, closure o strutture dati: i classici detached DOM nodes. In questo articolo vediamo come riconoscere il sintomo, perché succede e come usare un flusso di analisi con snapshot e confronti automatici tramite un agent integrato con Chrome DevTools per arrivare rapidamente al responsabile del leak.
Capita spesso: l’app sembra reattiva, poi dopo qualche minuto di click, aperture/chiusure di modali e navigazione interna inizia a “sedersi”. Lo scrolling scatta, le animazioni perdono frame, e anche azioni banali diventano meno immediate. Se il peggioramento è progressivo e correlato all’uso, il sospetto più comune è un memory leak.
Tra le cause più frequenti in un frontend moderno ci sono i detached DOM nodes: elementi rimossi dal DOM, ma ancora vivi in memoria perché qualche riferimento li trattiene. Sono come “fantasmi”: non li vedi più nell’interfaccia, ma continuano a occupare risorse.
Il colpevole tipico: un nodo DOM “staccato” ma ancora referenziato
Un detached DOM node nasce quando:
- un componente viene smontato o un modal viene chiuso;
- i nodi vengono effettivamente rimossi dalla pagina;
- qualcosa (listener, closure, cache, store, array) mantiene un riferimento a quei nodi o a oggetti che li referenziano.
Risultato: il garbage collector non può liberare quella memoria, perché dal punto di vista del runtime gli oggetti sono ancora raggiungibili.
Esempi ricorrenti:
- event listener aggiunti manualmente e mai rimossi;
- subscription a stream/event bus non “dispose” al teardown;
- setInterval / setTimeout che catturano riferimenti in closure;
- strutture dati globali (Map/Set/array) che accumulano elementi “chiusi” o non più utilizzati;
- riferimenti al DOM memorizzati in singleton, store o servizi.
Il problema pratico: trovarlo a mano è lento
La parte frustrante non è tanto sapere che c’è un leak, ma trovare dov’è.
L’analisi manuale richiede tipicamente:
- riprodurre un flusso (es. apri/chiudi un modal 20 volte);
- fare uno snapshot dell’heap;
- ripetere il flusso;
- fare un altro snapshot;
- confrontare i risultati e inseguire catene di riferimenti (retain paths).
È un lavoro meticoloso: basta perdersi un dettaglio e si ricomincia.
Un approccio più rapido: snapshot + confronto guidato da agent
Un modo molto più efficiente è delegare la parte “meccanica” a un agent integrato con DevTools: invece di setacciare a mano l’heap, l’agent può:
- creare automaticamente più heap snapshot in momenti chiave;
- confrontarli per evidenziare cosa cresce e non torna giù;
- puntare subito l’attenzione su oggetti sospetti (spesso proprio i detached DOM nodes);
- indicare l’elemento e le relazioni che lo trattengono in memoria.
In pratica, trasformi una caccia al tesoro in un flusso ripetibile: “fai A → snapshot → fai A → snapshot → dimmi cosa è rimasto”.
Strategia consigliata: riproduci, misura, isola
Anche con strumenti automatici, la qualità del risultato dipende da come imposti il test. Una procedura solida:
- Scegli un’azione ripetibile che rappresenti il problema (aprire/chiudere un dialog, cambiare route, montare/smontare una lista virtualizzata).
- Esegui un ciclo (10–30 ripetizioni) per amplificare il leak.
- Acquisisci snapshot prima e dopo.
- Confronta cercando:
- crescita costante di oggetti;
- presenza di detached nodes;
- listener o strutture dati che mantengono riferimenti.
Se dopo il ciclo la memoria non rientra (o rientra poco) e nello snapshot compaiono nodi “detached” che aumentano, hai un segnale forte.
Come chiudere davvero il leak: rimuovere la causa, non il sintomo
Quando individui il nodo o il componente che “resta appeso”, la fix tipica è una tra queste:
- rimuovere event listener nel teardown/unmount;
- disiscriversi da observer/subscription;
- cancellare timer e callback pendenti;
- eliminare riferimenti da cache/Map/Set quando l’elemento non serve più;
- evitare di memorizzare direttamente riferimenti DOM in strutture long-lived (o quantomeno gestirli esplicitamente).
Il punto chiave: non basta “nascondere” un pezzo di UI. Se l’albero DOM o gli oggetti correlati restano raggiungibili, resteranno anche in memoria.
Sintesi: meno caccia ai fantasmi, più metodo
I memory leak in frontend raramente sono misteriosi: spesso sono nodi DOM staccati trattenuti da riferimenti dimenticati. La differenza la fa il flusso di indagine.
Usare un approccio basato su heap snapshot e confronto, supportato da un agent che automatizza raccolta e analisi, riduce drasticamente il tempo necessario per arrivare al responsabile. E una volta identificata la catena di riferimenti, la correzione diventa quasi sempre un’operazione di “pulizia” nel lifecycle: unsubscribe, removeEventListener, clearInterval e gestione rigorosa delle strutture dati long-lived.
Se un’app diventa più lenta “col tempo”, non è un destino inevitabile: spesso è solo memoria che non viene mai lasciata andare.