Heap Dump: cos'è, analisi dell'heap e eliminazione delle perdite di memoria

Autore: IT Sectr Pubblicato: 2026-05-07 Tempo di lettura: 10 min

Heap Dump (dump dell'heap) è un'istantanea della memoria dinamica di un'applicazione contenente informazioni complete su tutti gli oggetti vivi: le loro classi, dimensioni, riferimenti reciproci e accessibilità dalle radici GC. Heap Dump è lo strumento principale per analizzare le perdite di memoria e ottimizzare il consumo di risorse. Secondo Android Developers, l'analisi degli heap dump consente di rilevare fino al 95% delle perdite di memoria, inclusi riferimenti ciclici, listener dimenticati e riferimenti statici non rilasciati.

Punti Chiave

  • Heap Dump è un'istantanea dell'intera memoria dinamica dell'applicazione con informazioni su ogni oggetto e sui riferimenti tra di essi.
  • Android Studio Memory Profiler consente di catturare heap dump in tempo reale per applicazioni Java e Kotlin.
  • Xcode Instruments fornisce lo strumento Allocations per creare e analizzare heap dump su iOS/macOS.
  • Shallow e retained size sono le metriche chiave: shallow è la dimensione dell'oggetto stesso, retained è la dimensione dell'oggetto più tutti gli oggetti che trattiene.
  • L'analisi di un heap dump include la ricerca nel dominator tree, gli oggetti retained più grandi e i percorsi più brevi verso le radici GC.

Cos'è un heap dump e a cosa serve

Heap dump è un dump completo dell'heap della macchina virtuale — l'area di memoria in cui risiedono tutti gli oggetti creati dinamicamente. In Java e Kotlin è l'heap Dalvik/ART su Android, in Swift e Objective-C è l'heap gestito da ARC su iOS. Un heap dump cattura ogni oggetto, la sua classe, dimensione, campi, riferimenti ad altri oggetti e flag di accessibilità dalle radici GC (variabili di stack, campi statici, riferimenti JNI).

Lo scopo principale di un heap dump è il rilevamento di perdite di memoria. Una perdita si verifica quando un'applicazione continua a mantenere riferimenti a oggetti che non sono più necessari, impedendo la loro raccolta da parte del garbage collector. Cause tipiche: listener di eventi non deregistrati alla distruzione di un'activity; singleton con riferimenti al contesto; closure che catturano self; collezioni statiche in cui i dati vengono aggiunti senza rimozione. Un heap dump fornisce un quadro preciso: quali oggetti sono “vivi”, quali sono superflui e chi li sta referenziando.

Secondo Google I/O, oltre il 60% dei rapporti di crash delle applicazioni Android sono correlati a OutOfMemoryError, e nell'80% dei casi la causa principale è una perdita di memoria rilevabile tramite heap dump. Per le applicazioni iOS la situazione è simile: le perdite dovute a retain cycle sono una delle cause più comuni di crash, identificate tramite lo strumento Allocations in Xcode.

Quando è necessario un heap dump

Un heap dump dovrebbe essere eseguito quando si presentano i seguenti sintomi: l'applicazione consuma memoria in modo lineare durante azioni ripetitive (navigazione avanti e indietro tra schermate); dopo la chiusura di una schermata, la memoria non torna al livello base; si verificano OutOfMemoryError o avvisi di memoria su iOS; l'applicazione termina per superamento del limite di memoria. La raccolta regolare di heap dump fa parte del protocollo di cultura ingegneristica in grandi progetti mobili come Instagram e Spotify.

Heap dump in Android Studio: ottenimento e analisi

Android Studio fornisce Memory Profiler — uno strumento integrato per catturare heap dump in tempo reale. Accessibile tramite View → Tool Windows → Profiler. Dopo aver avviato l'applicazione, selezionare la sessione, andare alla scheda Memory e fare clic su Dump Java Heap. Android Studio sospende l'applicazione, esegue un dump dell'heap ART e carica il risultato per l'analisi. Il file di dump è in formato .hprof — lo standard HPROF compatibile con la maggior parte degli analizzatori di memoria.

Dopo il caricamento del dump, Android Studio visualizza una tabella di oggetti con colonne: Allocations (numero di istanze), Native Size (memoria fuori dall'heap ART), Shallow Size (memoria dell'oggetto stesso), Retained Size (memoria dell'oggetto incluso l'intero sottografo). Il filtraggio per nome classe, l'ordinamento per retained size e la ricerca per pacchetti consentono di trovare rapidamente le aree problematiche.

kotlin
// Perdita tipica — un listener non deregistrato in onDestroy
class MainActivity : AppCompatActivity() {
    private val sensorManager by lazy {
        getSystemService(SENSOR_SERVICE) as SensorManager
    }
    private val listener = MySensorListener()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sensorManager.registerListener(listener,
            sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT),
            SensorManager.SENSOR_DELAY_NORMAL)
    }

    override fun onDestroy() {
        super.onDestroy()
        // ❌ Manca sensorManager.unregisterListener(listener)
        // → L'Activity non verrà raccolta dal GC, l'heap dump mostrerà la perdita
    }
}

Analisi del dominator tree in Android Studio

La scheda Dominator Tree mostra gli oggetti che trattengono la maggior quantità di memoria. Se un oggetto viene rimosso dal dominator tree, tutta la memoria che trattiene diventa disponibile per la raccolta. Questo è uno strumento chiave: invece di scansionare migliaia di oggetti, ci si concentra su 10–20 che controllano l'80–90% della memoria. Secondo Google, l'analisi del dominator tree è il modo più efficace per trovare un punto di perdita, riducendo il tempo di analisi da ore a minuti.

Heap dump in Xcode Instruments: Allocations e Leaks

Xcode Instruments fornisce due strumenti per lavorare con gli heap dump: Allocations — cattura di dump dell'heap con grafico del consumo in tempo reale; Leaks — ricerca automatica di perdite tramite analisi dei retain cycle. Allocations mostra tutti gli oggetti nell'heap, la loro dimensione, il numero di creazioni (allocations) e deallocazioni (deallocations). La differenza tra il numero di creazioni e deallocazioni per una classe specifica indica una potenziale perdita.

La cattura di un heap dump in Allocations viene eseguita con il pulsante Snapshot Memory — lo strumento sospende l'applicazione e scatta un dump completo. Successivamente, sono disponibili le viste standard: elenco di oggetti per classe, albero di chiamate (call tree) per ogni oggetto e un generatore di report. A differenza di Android Studio, Xcode non utilizza .hprof, ma memorizza i dati nel proprio formato .trace compatibile con Instruments.

swift
// Perdita iOS tipica — retain cycle tramite una closure
class NetworkManager {
    var onComplete: ((Data) -> Void)?

    func startRequest() {
        // ❌ La closure cattura self — retain cycle
        onComplete = { data in
            self.process(data)
        }
    }
    func process(_ data: Data) {}
}

Lo strumento Leaks rileva automaticamente retain cycle e perdite tramite l'analisi del grafo dei riferimenti. Marca gli oggetti che perdono con un'icona viola e mostra il percorso verso la radice (GC root). Per eliminare un retain cycle, è sufficiente aggiungere [weak self] o [unowned self] nella cattura della closure. L'esecuzione regolare dello strumento Leaks è un passaggio obbligatorio della pipeline CI nei team che utilizzano Swift per lo sviluppo iOS.

swift
// Correzione — riferimento debole a self
onComplete = { [weak self] data in
    guard let self else { return }
    self.process(data)
}

Shallow size, retained size e dominator tree

Per un'analisi corretta di un heap dump è necessario comprendere tre metriche chiave. Shallow size è la quantità di memoria occupata direttamente dall'oggetto: i suoi campi, l'intestazione (header) e l'allineamento. Per un tipico oggetto Java/Kotlin, la shallow size è di 16–40 byte. Retained size è la shallow size dell'oggetto più la somma della shallow size di tutti gli oggetti che sono accessibili solo attraverso questo oggetto (cioè diventerebbero spazzatura se venisse rimosso). La retained size mostra l'impatto reale dell'oggetto sul consumo di memoria.

MetricaDescrizioneEsempio
Shallow sizeDimensione dell'oggetto stesso in byteBitmap (100×100) = 40.016 B
Retained sizeShallow size + tutto ciò che trattieneActivity con View Tree = 2–5 MB
Deep sizeRetained size + oggetti annidati da altri grafiScrollView con adapter = 10–50 MB

Dominator tree è una struttura in cui ogni oggetto riferisce il suo “dominator” — l'oggetto che controlla la sua accessibilità. Se il dominator viene rimosso, tutti gli oggetti del suo sottoalbero diventano spazzatura. L'analisi del dominator tree è il modo più rapido per scoprire quale oggetto trattiene più memoria. Secondo Eclipse MAT (Memory Analyzer Tool), il 90% delle perdite viene rilevato esaminando la top-20 del dominator tree in 5 minuti.

Analisi delle perdite di memoria tramite heap dump

Il processo di analisi di una perdita tramite heap dump consiste in diversi passaggi. Passo 1: eseguire l'azione che dovrebbe liberare memoria (chiudere la schermata, terminare l'operazione). Passo 2: chiamare il GC ed eseguire un heap dump. Passo 3: trovare gli oggetti che avrebbero dovuto essere distrutti. Passo 4: per l'oggetto sospetto, eseguire Path to GC Roots — la catena di riferimenti che mantiene vivo l'oggetto. L'ultimo riferimento nella catena è la causa della perdita.

Path to GC Roots

La funzione Path to GC Roots è disponibile in Android Studio Profiler, Eclipse MAT e Xcode Instruments. Mostra la catena più breve di riferimenti da una radice GC all'oggetto problematico. Escludendo i riferimenti deboli (weak) e morbidi (soft), si ottengono solo quelli forti (strong) — quelli che impediscono effettivamente la raccolta. Secondo Square Engineering, il 70% delle perdite nelle applicazioni Android è causato da solo due modelli: riferimenti statici ad Activity o Context e listener registrati ma non deregistrati.

kotlin
// Esempio di perdita tramite riferimento statico
object AppCache {
    private val cache = mutableMapOf<String, Any>()

    fun storeActivityReference(activity: Activity) {
        cache["current_activity"] = activity // ❌ Perdita!
    }
}

// Correzione: riferimento debole
object AppCacheFixed {
    private val cache = mutableMapOf<String, WeakReference<Any>>()
}

Confronto di due heap dump

La tecnica della modalità di confronto è uno dei metodi più efficaci per trovare perdite. Eseguire un heap dump prima e dopo un'azione ripetitiva. Confrontare il numero di istanze delle classi chiave: se il numero di Activity è aumentato nonostante tutte le activity siano state chiuse — è una perdita. Android Studio ed Eclipse MAT supportano il confronto automatico dei dump con evidenziazione delle differenze. Secondo Google, il confronto dei dump consente di trovare perdite invisibili in un'analisi singola grazie all'effetto di accumulo.

Raccomandazioni pratiche per ridurre il consumo di memoria

Basandosi sull'analisi degli heap dump in progetti reali, sono state sviluppate pratiche comprovate di ottimizzazione della memoria. Utilizzare WeakReference per cache, callback e riferimenti al contesto in oggetti di lunga durata. Deregistrare i listener in onPause/onDestroy per Android e deinit per iOS. Evitare grandi collezioni statiche — se necessarie, utilizzare LruCache con limite di dimensione. Ottimizzare i Bitmap: caricare le immagini con il corretto inSampleSize, utilizzare Glide o Picasso con cache su disco.

Profilazione della memoria durante lo sviluppo

Includere la cattura regolare di heap dump nella propria pipeline CI. Impostare un'attività che esegua test UI strumentati, svolga gli scenari utente chiave e confronti l'heap dump con una baseline. Se la retained size cresce più del 5% rispetto alla baseline, la build viene contrassegnata come regressione. Questo approccio è praticato in Airbnb, Uber e altre aziende con elevati requisiti di qualità. Secondo Uber Engineering, l'implementazione dell'analisi automatica degli heap dump in CI ha ridotto i bug relativi alla memoria del 70% in un trimestre.

groovy
// Esempio di attività Gradle per heap dump automatico in CI
task profileMemory(type: Exec) {
    commandLine 'adb', 'shell',
        'am start -n com.example/.MainActivity'
    // In attesa del caricamento
    doLast {
        exec { commandLine 'adb', 'shell',
            'am broadcast -a com.example.DUMP_HEAP' }
    }
}

Domande Frequenti

Qual è la differenza tra shallow size e retained size?

Shallow size è la dimensione dell'oggetto stesso (campi + intestazione). Retained size è la dimensione dell'oggetto più tutti gli oggetti che diventerebbero spazzatura se venisse rimosso. La retained size è il principale indicatore dell'impatto di un oggetto sul consumo di memoria.

Come fare un heap dump su un dispositivo Android fisico?

Tramite Android Studio Profiler, selezionare il dispositivo e il processo, fare clic su Dump Java Heap. In alternativa, tramite riga di comando: adb shell am dumpheap PID /sdcard/dump.hprof, poi adb pull.

Perché un heap dump può essere enorme (500 MB+)?

Un heap dump include tutti gli oggetti vivi. Se l'applicazione utilizza cache, Bitmap o elabora grandi dati, il dump può raggiungere centinaia di megabyte. Filtrare per classi o utilizzare Eclipse MAT per caricare solo l'indice.

Si può analizzare un heap dump senza Android Studio?

Sì, utilizzare Eclipse MAT (Memory Analyzer Tool) — uno strumento gratuito per analizzare file .hprof. Supporta dominator tree, path to GC roots, confronto di dump e rilevamento automatico di perdite tramite Leak Suspects Report.

Un heap dump riduce le prestazioni dell'applicazione?

Il dump stesso — sì, perché la raccolta del dump sospende tutti i thread (stop-the-world). Senza dump — no. Eseguire i dump in ambienti controllati (banco di prova, CI), non in produzione.

Riepilogo

  • Heap Dump è un'istantanea completa dell'heap dell'applicazione con informazioni su ogni oggetto e le relazioni tra di essi.
  • Android Studio Memory Profiler e Xcode Instruments Allocations sono i principali strumenti di cattura dei dump.
  • Shallow size è la dimensione dell'oggetto stesso; retained size è la dimensione dell'oggetto con l'intero sottografo delle dipendenze.
  • Dominator tree mostra gli oggetti che controllano la maggior quantità di memoria.
  • Path to GC Roots è la catena di riferimenti forti che impedisce a un oggetto di essere raccolto.
  • Il confronto di due heap dump (prima/dopo un'azione) è il metodo più affidabile per rilevare perdite.
  • L'automazione della cattura e dell'analisi degli heap dump in CI previene le regressioni di memoria durante lo sviluppo.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche