🎉 Il Necronomicon è stato aggiornato alla versione 2.0.0 🎉
Memory Leaks
Introduzione

Memory Leaks

❗️

I memory leak esistono in molti linguaggi di programmazione, così come il concetto di garbage collection, in questo documento però analizzeremo nello specifico le dinamiche relative a JavaScript

Cosa sono i memory leaks?

I memory leaks sono delle problematiche relative alla gestione della memoria che si verificano quando un programma non libera la memoria che ha allocato. Questo comporta che la memoria allocata non sia più disponibile per essere utilizzata da altri programmi, e che quindi il programma che ha creato il memory leak utilizzi sempre più memoria, fino a quando non si esaurisce la memoria disponibile sul sistema.

Perché i memory leak esistono?

I linguaggi ad alto livello come per esempio JavaScript gestiscono la memoria tramite un garbage collector che si occupa di rimuovere dalla memoria i dati irraggiungibili

dunque per comprendere come si creano i memory leaks è necessario capire come funziona il garbage collector.

Innanzitutto è necessario sapere che all'interno del processo di esecuzione di JavaScript esistono 2 tipi di memoria:

  • Stack: memoria che viene allocata staticamente (di tipo LIFO)
  • Heap: memoria che viene allocata dinamicamente

All'interno della memoria stack risiedono tutte le variabili primitive (string, number e undefined) e i riferimenti alle variabili allocate nella memoria heap. Dato che, fino al momento dell'esecuzione, non si può sapere quanta memoria sarà necessaria per la computazione, all'interno della memoria heap risiedono tutte le rimanenti strutture dati (come oggetti, array ecc...)

Immaginiamo dunque di aver appena scritto questo pezzo di codice:

let a = 1
let b = 2

La nostra situazione sarà la seguente:

Passaggio 1

Dato che entrambe le veriabili sono primitive, vengono allocate nella memoria stack. Il garbage collector non ha quindi nulla da fare, e può continuare l'esecuzione del programma.

Ora immaginiamo di aver scritto questo pezzo di codice:

let a = 1
let b = 2
let c = { d: 3, e: 4 }

La nostra situazione sarà la seguente:

Passaggio 2

Come è possibile vedere dallo schema, il contenuto della variabile c viene mantenuto all'interno della memoria heap mentre, il suo riferimento viene mantenuto all'interno della memoria stack.

In questo caso quindi, dato che il riferimento all'oggetto appena creato ({d: 3, e: 4}) viene mantenuto all'interno della memoria stack, il garbage collector non può rimuoverlo, e quindi non può liberare la memoria allocata per l'oggetto.

Ma cosa succede quando assegnamo ad un'altra variabile la variabile c?

let a = 1
let b = 2
let c = { d: 3, e: 4 }
let f = c

Quello che succede all'interno della memoria è questo:

Passaggio 3

Cioè quindi non viene duplicato l'oggetto in memoria ma semplicemente viene fatto riferimento allo stesso oggetto allocato in heap

Ipotizziamo quindi che la variabile c non venga più utilizzata, e che quindi venga rimosso il suo riferimento dalla memoria stack:

let a = 1
let b = 2
let c = { d: 3, e: 4 }
let f = c
c = null

La nostra situazione sarà la seguente:

Passaggio 4

Questo ci porta quindi a un possibile problema per il garbage collector: nonostante il riferimento orginale all'oggetto sia stato rimosso, il garbage collector non può rimuovere l'oggetto in quanto esiste ancora un riferimento all'oggetto.

In questo caso, ovviamente, è riconoscibile il riferimento che mantiene ancorato l'oggetto in memoria, ma in molti casi non è così semplice.

Nel caso in cui, quindi, si venissero a creare molte di queste situazioni, magari all'interno di funzioni usate ricorrentemente, il garbage collector non sarebbe in grado di liberare la memoria allocata e quindi generando un memory leak