Ogni programma che eseguiamo, grande o piccolo, ha bisogno di memoria per elaborare. Tuttavia, quando un programma usa memoria e si dimentica di liberarla dopo l'utilizzo, si verifica un memory leak.
Col tempo, più memory leak possono causare carenza di spazio e l'impossibilità di elaborare le attività in arrivo. Ecco perché rilevare e gestire i memory leak è fondamentale per evitare errori inutili.
In questo articolo, esploreremo a fondo i memory leak. Iniziamo!
Cos'è un memory leak?
I memory leak si verificano quando un programma o un'applicazione utilizza memoria e non la libera dopo l'uso. Per memoria intendo la RAM: non confonderla con la memoria del disco rigido. Queste perdite si accumulano gradualmente, lasciando la RAM troppo piena per gestire nuovi processi.
I memory leak prosciugano la RAM e impattano le prestazioni aumentando le operazioni di I/O. Man mano che i memory leak si accumulano, il sistema prova a liberare RAM scaricando dati sul disco, con un conseguente aumento delle operazioni di I/O su disco.
Anche la sicurezza è a rischio quando i memory leak trattengono dati sensibili. Se informazioni come password o chiavi di cifratura rimangono in RAM più del necessario, sono più vulnerabili ad attacchi.
Esempi di memory leak nei linguaggi di programmazione più diffusi
Per capire meglio come avvengono i memory leak, non c'è niente di meglio che vederli in azione con alcuni esempi.
Memory leak in Python
Python si affida al conteggio dei riferimenti per la gestione della memoria. Rimuove un oggetto quando il suo conteggio dei riferimenti—il numero di altri oggetti che vi fanno riferimento—scende a zero. Tuttavia, ha alcune limitazioni, come l'incapacità di gestire i riferimenti circolari.
Per chi non conosce il termine, un riferimento circolare si verifica quando due o più variabili si riferiscono l'una all'altra in modo circolare.
Per esempio, l'oggetto a fa riferimento a b, b fa riferimento a c e c fa nuovamente riferimento ad a, creando un ciclo infinito. Qui, il conteggio dei riferimenti non può mai diventare 0 e gli oggetti restano in memoria per sempre, causando memory leak in Python.
classNode:def__init__(self, value):
self.value = value
self.next=None# Create two nodes
node1 = Node(1)
node2 = Node(2)# Establish a circular reference
node1.next= node2 # node1 refers to node2
node2.next= node1 # node2 refers back to node1Offerto da
Il frammento di codice sopra mostra un riferimento circolare. Python ha un garbage collector per gestire questi scenari. Tuttavia, se non riesce a gestire scenari specifici, come quando si usano variabili globali, dovresti gestire manualmente i memory leak impostando a None i riferimenti agli oggetti non più utilizzati.
Java gestisce automaticamente la memoria e non richiede aiuto esplicito dai programmatori. Tuttavia, in casi come registrazioni errate dei listener o riferimenti statici, il garbage collector può non riuscire a liberare lo spazio, causando memory leak. Vediamo come può accadere:
Una sorgente di eventi genera eventi e un oggetto listener si registra per essere notificato quando quegli eventi si verificano.
Se la sorgente di eventi mantiene un riferimento forte al listener, il listener non può essere raccolto fino a quando la sorgente è attiva, anche se il listener non è più utilizzato.
In tali casi, possono verificarsi memory leak. Per evitarli, annulla manualmente la registrazione dei listener quando non servono più.
Un'altra causa comune di memory leak sono le variabili statiche. Le variabili statiche restano in memoria per l'intero ciclo di vita. Quindi, se fanno riferimento a oggetti, questi non verranno raccolti, anche se non più in uso, causando memory leak. Per esempio:
importjava.util.ArrayList;importjava.util.List;publicclassStaticMemoryLeak{// Static listprivatestaticList<Object> staticList =newArrayList<>();publicstaticvoidaddObject(Object obj){// Add the object to the static list
staticList.add(obj);}publicstaticvoidmain(String[] args){for(int i =0; i <1000; i++){Object obj =newbyte[1024*1024];// Add the object to the static listaddObject(obj);}}}Offerto da
Nel programma sopra, la variabile staticList è dichiarata come array statico e vi vengono aggiunti oggetti. Il ciclo viene eseguito 1000 volte, aggiungendo ogni volta oggetti da 1 MB, per un totale di 1 GB di memoria.
Poiché la lista è statica, non perderà mai i riferimenti agli oggetti finché il programma è in esecuzione. Quindi, il garbage collector non può eliminare quegli oggetti e 1 GB di memoria resterà occupato per l'intero ciclo di vita. Questa memoria inutilizzata può causare errori OutOfMemoryError. Per risolvere, dovresti chiamare esplicitamente staticList.clear() e rilasciare la memoria.
Memory leak in C/C++
A differenza di Java e Python, C++ non ha garbage collector automatici. Quindi, i memory leak in C++ si verificano quando i programmatori allocano memoria e si dimenticano di deallocarla. Per esempio,
voidsample_leak(){int* ptr =newint(5);return;}Offerto da
Nel frammento sopra, il puntatore contiene l'indirizzo di memoria in cui è memorizzato l'intero 5. Quando la funzione termina, la variabile locale ptr esce dallo scope, ma la memoria allocata sull'heap rimane, causando un memory leak.
Per evitarlo, aggiungi esplicitamente la riga delete ptr; per deallocare la memoria.
Memory leak in JavaScript
Javascript ha un garbage collector che gestisce automaticamente la memoria. Funziona così: trova la radice, segue ricorsivamente il suo percorso verso i figli e contrassegna ogni figlio come attivo o inattivo. Infine, elimina tutti i riferimenti inattivi.
Tuttavia, il garbage collector può non riuscire a rilasciare la memoria nei seguenti casi:
Variabili globali: quando dimentichi di dichiarare una variabile con let, var o const, Javascript la crea automaticamente come variabile globale. Finché il programma è in esecuzione, le variabili globali restano in memoria e sono sempre raggiungibili, impedendo al garbage collector di rilasciarle.
setTimeOut():setTimeOut() pianifica l'esecuzione di una funzione di callback dopo un certo tempo. Un memory leak può verificarsi quando questa funzione di callback mantiene il riferimento per più tempo. Ecco un esempio:
functionTimeoutExample(){var obj =10;setTimeout(function(){
console.log(obj);},1000);// This runs after 1 second}TimeoutExample();Offerto da
Nel codice sopra, anche dopo che la funzione TimeoutExample() termina, obj mantiene ancora un riferimento fino all'esecuzione della funzione di callback perché obj è utilizzato all'interno della callback. Se ciò accade con oggetti di grandi dimensioni, porta a seri memory leak.
Cause dei memory leak
Come detto, i memory leak possono verificarsi in vari linguaggi di programmazione per ragioni diverse. Alcuni linguaggi non hanno la raccolta automatica della memoria, mentre altri possono non riuscire a rilasciare memoria in scenari anomali.
Vediamo nel dettaglio alcune cause comuni:
Riferimenti non rilasciati: ogni volta che creiamo oggetti o variabili, il sistema alloca memoria o riferimenti per essi. Non chiudere quei riferimenti dopo l'uso può bloccare la memoria. Questo tipo di memoria si accumula nel tempo e porta a problemi di memoria.
Riferimenti circolari: si verificano quando più oggetti si referenziano a vicenda, formando un ciclo. Anche se gli oggetti non sono più in uso, restano referenziati da altri oggetti nel ciclo, impedendo al garbage collector di recuperare la loro memoria.
Gestione impropria delle risorse: a volte non ci preoccupiamo di chiudere correttamente connessioni al database, socket di rete o file handle dopo l'uso. Questa negligenza può portare a seri problemi di memory leak. Per esempio, l'OS ha un numero limitato di descrittori di file per gestire i file aperti. Se i file non vengono mai chiusi, l'OS esaurisce i descrittori, impedendo al sistema di aprire nuovi file.
Uso improprio di variabili statiche: i memory leak si verificano spesso a causa dell'uso eccessivo di variabili statiche. Poiché le variabili statiche restano vive per l'intera durata del programma, qualunque oggetto a cui fanno riferimento rimane in memoria—anche quando non è in uso. Per prevenire i memory leak, questi oggetti devono essere rilasciati esplicitamente o va evitato l'uso di variabili statiche.
Librerie e framework esterni: librerie di terze parti e framework esterni possono introdurre memory leak a causa di una gestione inefficiente delle risorse. I memory leak si verificano se utilizzano le risorse del tuo sistema e non le rilasciano.
Come rilevare i memory leak?
Ora che abbiamo esaminato perché possono verificarsi memory leak, discuteremo alcuni metodi affidabili per individuarli e farne il debug.
Ispezione manuale: rivedi con attenzione il codice per identificare potenziali cause di memory leak, come riferimenti circolari, variabili statiche, istruzioni di deallocazione mancanti o variabili che mantengono riferimenti a oggetti inutilizzati. Ispezionando a fondo questi pattern nel codice, puoi individuare il problema esatto e affrontarlo proattivamente.
Uso di strumenti di debug: esistono vari strumenti e librerie di debug specifici per i diversi linguaggi di programmazione per rilevare i memory leak. Per esempio, tracemalloc di Python ci consente di confrontare snapshot di memoria in momenti diversi e monitorare le allocazioni tra questi intervalli. Inoltre, il memory profiler di Python ci permette di eseguire il debug riga per riga e individuare aree con uso di memoria insolitamente elevato.
Monitoraggio dell'uso di memoria: gli strumenti di monitoraggio della memoria controllano proattivamente il consumo di memoria e individuano problemi in tempo reale. Questi strumenti ti avvisano di problemi critici di memoria e forniscono log pertinenti, insieme a suggerimenti su come risolverli. Esempi includono Valgrind, Paessler, AddressSanitizer e altri.
Scrivere test per le perdite: considera di aggiungere unit test che rilevino i memory leak. In questo modo, oltre ai problemi di funzionalità e performance, i memory leak vengono individuati durante lo sviluppo.
Scrivere test di integrazione: i test di integrazione dovrebbero essere usati anche per simulare scenari reali e rilevare potenziali memory leak che potrebbero emergere in produzione.
Best practice per prevenire i memory leak
I memory leak sono comuni nelle applicazioni che si basano su risorse di memoria. Ecco alcune pratiche che puoi seguire per prevenirli.
Gestione efficiente delle risorse
Prova a usare linguaggi come Java o Python che gestiscono automaticamente la memoria tramite garbage collector. Altrimenti, in linguaggi come C o C++, assicurati che tutta la memoria allocata venga esplicitamente deallocata.
Inoltre, usa costrutti come i blocchi finally o i context manager come with in Python o try-with-resources in Java per una pulizia efficiente delle risorse. Queste pratiche aiutano a prevenire i memory leak e a garantire una corretta gestione delle risorse.
Usa riferimenti deboli
A differenza dei riferimenti forti, i riferimenti deboli non impediscono che gli oggetti vengano raccolti dal garbage collector. Anche se mantengono riferimenti agli oggetti, non contribuiscono alla raggiungibilità dell'oggetto. Quindi, il garbage collector può reclamare la memoria associata a quell'oggetto. Perciò, usa riferimenti deboli invece di quelli forti.
Revisioni del codice frequenti
È importante condurre revisioni del codice regolari per identificare i memory leak. Durante la revisione, cerca pattern comuni come risorse non chiuse, riferimenti circolari, variabili globali o statiche e caching inefficiente. In questo modo, troverai rapidamente il problema prima che impatti il tuo sistema.
Conclusione
In questo articolo, abbiamo esplorato gli aspetti fondamentali dei memory leak, dalle cause e gli esempi alle tecniche di rilevamento. Abbiamo visto come diversi scenari possano portare a memory leak e come puoi gestirli. Inoltre, abbiamo discusso le best practice da seguire per prevenire i memory leak in futuro.
Dato che abbiamo esplorato i memory leak attraverso diversi linguaggi di programmazione, ti consiglio di approfondire con i seguenti corsi:
Alcuni sintomi comuni indicano che i memory leak stanno influenzando il tuo sistema: rallentamento del sistema, browser lenti, crash di applicazioni o del sistema operativo.
Quali linguaggi sono più inclini ai memory leak?⌄
Linguaggi come C e C++ sono inclini ai memory leak perché non hanno garbage collector che gestiscono automaticamente la memoria. Al contrario, devi allocare e deallocare la memoria manualmente.
I memory leak possono verificarsi nei linguaggi di programmazione moderni?⌄
Sì! La raccolta automatica della memoria nei linguaggi di programmazione moderni non riesce a gestire scenari come registrazioni improprie di event listener e variabili statiche o globali.
Come risolvo un memory leak?⌄
Per correggere un memory leak, segui questi passaggi:
Identifica la perdita usando strumenti di profiling.
Analizza la causa, ad esempio file handle non chiusi o riferimenti persistenti.
Libera memoria esplicitamente in linguaggi come C/C++ o gestisci correttamente i riferimenti nei linguaggi con garbage collector.
Ottimizza la gestione della memoria utilizzando best practice come riferimenti deboli e una corretta gestione del ciclo di vita degli oggetti.
Srujana è una tech writer freelance con una laurea quadriennale in Informatica. Scrivere di vari argomenti, tra cui data science, cloud computing, sviluppo, programmazione, sicurezza e molti altri, le viene naturale. Ama la letteratura classica ed esplorare nuove destinazioni.
Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Ogni programma che eseguiamo, grande o piccolo, ha bisogno di memoria per elaborare. Tuttavia, quando un programma usa memoria e si dimentica di liberarla dopo l'utilizzo, si verifica un memory leak.
Col tempo, più memory leak possono causare carenza di spazio e l'impossibilità di elaborare le attività in arrivo. Ecco perché rilevare e gestire i memory leak è fondamentale per evitare errori inutili.
In questo articolo, esploreremo a fondo i memory leak. Iniziamo!
Cos'è un memory leak?
I memory leak si verificano quando un programma o un'applicazione utilizza memoria e non la libera dopo l'uso. Per memoria intendo la RAM: non confonderla con la memoria del disco rigido. Queste perdite si accumulano gradualmente, lasciando la RAM troppo piena per gestire nuovi processi.
I memory leak prosciugano la RAM e impattano le prestazioni aumentando le operazioni di I/O. Man mano che i memory leak si accumulano, il sistema prova a liberare RAM scaricando dati sul disco, con un conseguente aumento delle operazioni di I/O su disco.
Anche la sicurezza è a rischio quando i memory leak trattengono dati sensibili. Se informazioni come password o chiavi di cifratura rimangono in RAM più del necessario, sono più vulnerabili ad attacchi.
Esempi di memory leak nei linguaggi di programmazione più diffusi
Per capire meglio come avvengono i memory leak, non c'è niente di meglio che vederli in azione con alcuni esempi.
Memory leak in Python
Python si affida al conteggio dei riferimenti per la gestione della memoria. Rimuove un oggetto quando il suo conteggio dei riferimenti—il numero di altri oggetti che vi fanno riferimento—scende a zero. Tuttavia, ha alcune limitazioni, come l'incapacità di gestire i riferimenti circolari.
Per chi non conosce il termine, un riferimento circolare si verifica quando due o più variabili si riferiscono l'una all'altra in modo circolare.
Per esempio, l'oggetto a fa riferimento a b, b fa riferimento a c e c fa nuovamente riferimento ad a, creando un ciclo infinito. Qui, il conteggio dei riferimenti non può mai diventare 0 e gli oggetti restano in memoria per sempre, causando memory leak in Python.
Il frammento di codice sopra mostra un riferimento circolare. Python ha un garbage collector per gestire questi scenari. Tuttavia, se non riesce a gestire scenari specifici, come quando si usano variabili globali, dovresti gestire manualmente i memory leak impostando a
Nonei riferimenti agli oggetti non più utilizzati.Ti consiglio di imparare a scrivere classi efficienti in memoria in Python per velocizzare il runtime consumando meno risorse.
Memory leak in Java
Java gestisce automaticamente la memoria e non richiede aiuto esplicito dai programmatori. Tuttavia, in casi come registrazioni errate dei listener o riferimenti statici, il garbage collector può non riuscire a liberare lo spazio, causando memory leak. Vediamo come può accadere:
In tali casi, possono verificarsi memory leak. Per evitarli, annulla manualmente la registrazione dei listener quando non servono più.
Un'altra causa comune di memory leak sono le variabili statiche. Le variabili statiche restano in memoria per l'intero ciclo di vita. Quindi, se fanno riferimento a oggetti, questi non verranno raccolti, anche se non più in uso, causando memory leak. Per esempio:
Nel programma sopra, la variabile
staticListè dichiarata come array statico e vi vengono aggiunti oggetti. Il ciclo viene eseguito 1000 volte, aggiungendo ogni volta oggetti da 1 MB, per un totale di 1 GB di memoria.Poiché la lista è statica, non perderà mai i riferimenti agli oggetti finché il programma è in esecuzione. Quindi, il garbage collector non può eliminare quegli oggetti e 1 GB di memoria resterà occupato per l'intero ciclo di vita. Questa memoria inutilizzata può causare errori
OutOfMemoryError. Per risolvere, dovresti chiamare esplicitamentestaticList.clear()e rilasciare la memoria.Memory leak in C/C++
A differenza di Java e Python, C++ non ha garbage collector automatici. Quindi, i memory leak in C++ si verificano quando i programmatori allocano memoria e si dimenticano di deallocarla. Per esempio,
Nel frammento sopra, il puntatore contiene l'indirizzo di memoria in cui è memorizzato l'intero 5. Quando la funzione termina, la variabile locale
ptresce dallo scope, ma la memoria allocata sull'heap rimane, causando un memory leak.Per evitarlo, aggiungi esplicitamente la riga
delete ptr;per deallocare la memoria.Memory leak in JavaScript
Javascript ha un garbage collector che gestisce automaticamente la memoria. Funziona così: trova la radice, segue ricorsivamente il suo percorso verso i figli e contrassegna ogni figlio come attivo o inattivo. Infine, elimina tutti i riferimenti inattivi.
Tuttavia, il garbage collector può non riuscire a rilasciare la memoria nei seguenti casi:
let,varoconst, Javascript la crea automaticamente come variabile globale. Finché il programma è in esecuzione, le variabili globali restano in memoria e sono sempre raggiungibili, impedendo al garbage collector di rilasciarle.setTimeOut()pianifica l'esecuzione di una funzione di callback dopo un certo tempo. Un memory leak può verificarsi quando questa funzione di callback mantiene il riferimento per più tempo. Ecco un esempio:Nel codice sopra, anche dopo che la funzione
TimeoutExample()termina,objmantiene ancora un riferimento fino all'esecuzione della funzione di callback perchéobjè utilizzato all'interno della callback. Se ciò accade con oggetti di grandi dimensioni, porta a seri memory leak.Cause dei memory leak
Come detto, i memory leak possono verificarsi in vari linguaggi di programmazione per ragioni diverse. Alcuni linguaggi non hanno la raccolta automatica della memoria, mentre altri possono non riuscire a rilasciare memoria in scenari anomali.
Vediamo nel dettaglio alcune cause comuni:
Come rilevare i memory leak?
Ora che abbiamo esaminato perché possono verificarsi memory leak, discuteremo alcuni metodi affidabili per individuarli e farne il debug.
tracemallocdi Python ci consente di confrontare snapshot di memoria in momenti diversi e monitorare le allocazioni tra questi intervalli. Inoltre, il memory profiler di Python ci permette di eseguire il debug riga per riga e individuare aree con uso di memoria insolitamente elevato.Best practice per prevenire i memory leak
I memory leak sono comuni nelle applicazioni che si basano su risorse di memoria. Ecco alcune pratiche che puoi seguire per prevenirli.
Gestione efficiente delle risorse
Prova a usare linguaggi come Java o Python che gestiscono automaticamente la memoria tramite garbage collector. Altrimenti, in linguaggi come C o C++, assicurati che tutta la memoria allocata venga esplicitamente deallocata.
Inoltre, usa costrutti come i blocchi finally o i context manager come
within Python otry-with-resourcesin Java per una pulizia efficiente delle risorse. Queste pratiche aiutano a prevenire i memory leak e a garantire una corretta gestione delle risorse.Usa riferimenti deboli
A differenza dei riferimenti forti, i riferimenti deboli non impediscono che gli oggetti vengano raccolti dal garbage collector. Anche se mantengono riferimenti agli oggetti, non contribuiscono alla raggiungibilità dell'oggetto. Quindi, il garbage collector può reclamare la memoria associata a quell'oggetto. Perciò, usa riferimenti deboli invece di quelli forti.
Revisioni del codice frequenti
È importante condurre revisioni del codice regolari per identificare i memory leak. Durante la revisione, cerca pattern comuni come risorse non chiuse, riferimenti circolari, variabili globali o statiche e caching inefficiente. In questo modo, troverai rapidamente il problema prima che impatti il tuo sistema.
Conclusione
In questo articolo, abbiamo esplorato gli aspetti fondamentali dei memory leak, dalle cause e gli esempi alle tecniche di rilevamento. Abbiamo visto come diversi scenari possano portare a memory leak e come puoi gestirli. Inoltre, abbiamo discusso le best practice da seguire per prevenire i memory leak in futuro.
Dato che abbiamo esplorato i memory leak attraverso diversi linguaggi di programmazione, ti consiglio di approfondire con i seguenti corsi: