Best practice di gestione della memoria

Questo documento presuppone che tu abbia seguito le indicazioni sulle best practice per le app per Android in materia di gestione della memoria, ad esempio Gestire la memoria dell'app.

Introduzione

Una fuga di memoria è un tipo di perdita di risorse che si verifica quando un programma per computer non libera la memoria allocata che non è più necessaria. Una perdita può portare l'applicazione a richiedere all'OS più memoria di quella disponibile, causandone l'arresto anomalo. Una serie di pratiche improprie possono causare fughe di memoria nelle app per Android, ad esempio lo smaltimento non corretto o di non annullare la registrazione dei listener quando non sono più necessari.

Questo documento fornisce alcune best practice per aiutarti a prevenire, rilevare e risolvere le perdite di memoria nel codice. Se hai provato i metodi indicati in questa documentare e sospettare una perdita di memoria nei nostri SDK, vedi Come segnalare problemi relativi agli SDK Google.

Prima di contattare l'assistenza

Prima di segnalare una perdita di memoria al team dell'Assistenza Google, segui le best practice e i passaggi di debug forniti in questo documento per assicurati che l'errore non sia nel tuo codice. Questi passaggi potrebbero risolvere il problema e, se non lo fanno, generano le informazioni di cui il team dell'Assistenza Google ha bisogno per aiutarti.

Evitare perdite di memoria

Segui queste best practice per evitare alcune delle cause più comuni di perdite di memoria nel codice che usa gli SDK Google.

Best practice per le app per Android

Verifica di aver eseguito tutti i seguenti passaggi nella tua applicazione per Android:

  1. Rilascia le risorse inutilizzate.
  2. Annullare la registrazione degli ascoltatori quando non sono più necessari.
  3. Annullare le attività quando non sono necessarie.
  4. Inoltrare i metodi del ciclo di vita per rilasciare risorse.
  5. Utilizzare le versioni più recenti degli SDK

Per dettagli specifici su ciascuna di queste pratiche, consulta le sezioni seguenti.

Rilascia risorse inutilizzate

Quando la tua app per Android utilizza una risorsa, assicurati di rilasciarla quando non è più necessaria. In caso contrario, la risorsa continuerà a occupare memoria anche al termine dell'utilizzo da parte dell'applicazione. Per ulteriori informazioni, consulta Il ciclo di vita dell'attività nella documentazione di Android.

Rilascia riferimenti GoogleMap inattivi in GeoSDK

Un errore comune è che una mappa Google può causare una perdita di memoria se memorizzata nella cache utilizzando NavigationView o MapView. Una mappa di Google Maps ha una relazione 1:1 con NavigazioneView o MapView da cui viene recuperata. Tu deve garantire che una mappa di Google Maps non sia memorizzata nella cache o che il riferimento sia viene rilasciato quando viene chiamato NavigationView#onDestroy o MapView#onDestroy. Se utilizzando NavigationSupportFragment, MapSupportFragment o il tuo frammento che raggruppano queste viste, il riferimento deve essere rilasciato Fragment#onDestroyView.

class NavFragment : SupportNavigationFragment() {

  var googleMap: GoogleMap?

  override fun onCreateView(
    inflater: LayoutInflater,
    parent: ViewGroup?,
    savedInstanceState: Bundle?,
  ): View  {
    super.onCreateView(inflater,parent,savedInstanceState)
    getMapAsync{map -> googleMap = map}
  }

  override fun onDestroyView() {
    googleMap = null
  }
}

Annulla la registrazione dei listener quando non sono più necessari

Quando la tua app per Android registra un listener per un evento, ad esempio un pulsante. un clic o una modifica dello stato di una vista, assicurati di annullare la registrazione del listener quando l'applicazione non ha più bisogno di monitorare l'evento. In caso contrario, gli ascoltatori continueranno a occupare memoria anche dopo che l'applicazione li ha utilizzati.

Ad esempio, supponiamo che la tua applicazione utilizzi l'SDK Navigation e chiami il seguente listener per rilevare gli eventi di arrivo: metodo addArrivalListener per rilevare gli eventi di arrivo, deve anche chiamare removeArrivalListener quando non è più necessario monitorare gli eventi di arrivo.

var arrivalListener: Navigator.ArrivalListener? = null

fun registerNavigationListeners() {
  arrivalListener =
    Navigator.ArrivalListener {
      ...
    }
  navigator.addArrivalListener(arrivalListener)
}

override fun onDestroy() {
  navView.onDestroy()
  if (arrivalListener != null) {
    navigator.removeArrivalListener(arrivalListener)
  }

  ...
  super.onDestroy()
}

Annullare le attività quando non sono necessarie

Quando un'app per Android avvia un'attività asincrona, ad esempio un download o una richiesta di rete, assicurati di annullarla al termine. Se l'attività non viene annullato, continua a essere eseguito in background anche dopo ha finito con questo.

Per ulteriori dettagli sulle best practice, consulta Gestire la memoria dell'app nella documentazione di Android.

Inoltra i metodi del ciclo di vita per rilasciare risorse

Se la tua app utilizza l'SDK di navigazione o Maps, assicurati di rilasciare l'elemento inoltrando i metodi del ciclo di vita (mostrati in grassetto) a navView. Puoi farlo utilizzando NavigationView nell'SDK Navigation o MapView nell'SDK Maps o Navigation. Puoi anche utilizzare SupportNavigationFragment o SupportMapFragment anziché NavigationView e MapView, rispettivamente. I frammenti di supporto gestiscono inoltro dei metodi di ciclo di vita.

class NavViewActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    navView = ...
    navView.onCreate(savedInstanceState)
    ...
  }

  override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)
    navView.onSaveInstanceState(savedInstanceState)
  }

  override fun onTrimMemory(level: Int) {
    super.onTrimMemory(level)
    navView.onTrimMemory(level)
  }

  /* Same with
    override fun onStart()
    override fun onResume()
    override fun onPause()
    override fun onConfigurationChanged(...)
    override fun onStop()
    override fun onDestroy()
  */
}

Utilizzare le versioni più recenti degli SDK

Gli SDK Google vengono costantemente aggiornati con nuove funzionalità, correzioni di bug e miglioramenti delle prestazioni. Per ricevere questi SDK, mantieni aggiornati gli SDK nella tua app correzioni.

Debug delle fughe di memoria

Se continui a riscontrare perdite di memoria dopo aver implementato tutti i suggerimenti applicabili riportati in precedenza in questo documento, segui questa procedura per eseguire il debug.

Prima di iniziare, devi conoscere la modalità di gestione della memoria da parte di Android. Per informazioni, consulta la Panoramica della gestione della memoria di Android.

Per eseguire il debug delle perdite di memoria, segui questa procedura:

  1. Riproduci il problema. Questo passaggio è fondamentale per il debug.
  2. Controlla se l'utilizzo della memoria è previsto. Verifica che l'aumento dell'utilizzo che sembra essere una perdita non sia in realtà la memoria richiesta per l'esecuzione dell'applicazione.
  3. Esegui il debug a livello generale. Esistono diverse utilità che puoi utilizzare per eseguire il debug. Tre diversi set di strumenti standard consentono di eseguire il debug dei problemi di memoria in Android: Android Studio, Perfetto e Android Debug Bridge (adb) utilità della riga di comando.
  4. Controlla la memoria utilizzata dall'app. Ottieni un dump dell'heap e il monitoraggio dell'allocazione, quindi analizzali.
  5. Risolvi le perdite di memoria.

Questi passaggi vengono descritti in dettaglio nelle sezioni seguenti.

Passaggio 1: riproduci il problema

Se non sei riuscito a ricreare il problema, considera innanzitutto gli scenari che potrebbero causare perdite di memoria. per guardare subito un dump dell'heap potrebbe funzionare, se sai che il problema è stato ricreato. Tuttavia, se si ottiene un dump dell'heap all'avvio dell'app o un altro momento casuale, potresti non aver attivato le condizioni per attivare una fuga di dati. Valuta la possibilità di provare diversi scenari quando cerchi di ricreare il problema:

  • Quale insieme di funzionalità è attivato?

  • Quale sequenza specifica di azioni degli utenti provoca la fuga di dati?

    • Hai provato più volte ad attivare questa sequenza?
  • Quali stati del ciclo di vita ha attraversato l'app?

    • Hai provato più iterazioni in stati del ciclo di vita diversi?

Assicurati di poter ricreare il problema nell'ultima versione degli SDK. Il problema di una versione precedente potrebbe essere già stato risolto.

Passaggio 2: controlla se l'utilizzo della memoria per l'app è previsto

Ogni funzionalità richiede memoria aggiuntiva. Quando esegui il debug di diversi scenari, valuta se si tratta di un utilizzo previsto o meno di fuga di memoria. Ad esempio, per le diverse funzionalità o attività utente, considera la le seguenti possibilità:

  • Probabile perdita: l'attivazione dello scenario tramite più iterazioni porta a un aumento dell'utilizzo della memoria nel tempo.

  • Utilizzo della memoria probabilmente previsto: la memoria viene recuperata dopo l'interruzione dello scenario.

  • Possibile utilizzo della memoria previsto: aumenti dell'utilizzo della memoria per un periodo di per poi diminuire. Ciò potrebbe essere dovuto a una cache limitata o ad altre memoria utilizzata.

Se il comportamento dell'app è probabilmente un utilizzo della memoria previsto, il problema può essere risolto gestendo la memoria dell'app. Per assistenza, consulta Gestire la memoria dell'app.

Passaggio 3: esegui il debug a un livello elevato

Quando esegui il debug di una perdita di memoria, inizia a livello generale e poi visualizza in dettaglio dopo aver ristretto le possibilità. Utilizza una delle seguenti opzioni Strumenti di debug per l'analisi iniziale di eventuali perdite nel tempo:

Profiler di memoria di Android Studio

Questo strumento fornisce un istogramma visivo della memoria consumata. Anche i dump dell'heap e il monitoraggio dell'allocazione possono essere attivati dalla stessa interfaccia. Questo strumento è il consiglio predefinito. Per ulteriori informazioni, vedi Profilo memoria di Android Studio.

Contatori memoria Perfetto

Perfetto ti offre un controllo preciso sul monitoraggio di diverse metriche e le presenta tutte in un unico istogramma. Per ulteriori informazioni, vedi Contatori memoria Perfetto.

Interfaccia utente di Perfetto

Utilità a riga di comando Android Debug Bridge (adb)

Gran parte di ciò che puoi tracciare con Perfetto è disponibile anche come adb a riga di comando su cui puoi eseguire query direttamente. Un paio di aspetti Ecco alcuni esempi:

  • Meminfo ti consente di vedere informazioni dettagliate sulla memoria in un determinato momento.

  • Procstats fornisce alcune statistiche aggregate importanti nel tempo.

Una statistica fondamentale da esaminare qui è l'impronta in memoria fisica massima (maxRSS) richiesta dall'app nel tempo. Il valore MaxPSS potrebbe non essere così preciso. Per un modo per aumentare la precisione, consulta il flag adb shell dumpsys procstats --help –start-testing.

Monitoraggio dell'allocazione

Il monitoraggio dell'allocazione identifica l'analisi dello stack in cui è stata allocata la memoria. se non fosse stato liberato. Questo passaggio è particolarmente utile per individuare le perdite nei codice nativo. Poiché questo strumento identifica l'analisi dello stack, può essere un'ottima soluzione significa eseguire rapidamente il debug della causa principale o capire come ricreare il problema. Per la procedura da seguire per utilizzare il monitoraggio dell'allocazione, consulta Eseguire il debug della memoria nel codice nativo con il monitoraggio dell'allocazione.

Passaggio 4: controlla l'utilizzo della memoria dell'app con un dump dell'heap

Un modo per rilevare una perdita di memoria è generare un dump dell'heap della tua app e poi controllarla per rilevare eventuali perdite. Un dump dell'heap è uno snapshot di tutti gli oggetti nella memoria di un'app. Può essere usato per diagnosticare perdite di memoria e altre problemi di memoria.

Android Studio è in grado di rilevare perdite di memoria non risolvibili da GC. Quando acquisisci un dump dell'heap, Android Studio controlla se è presente un'attività o un frammento ancora raggiungibile, ma già distrutto.

  1. Acquisisci un dump dell'heap.
  2. Analizza il dump dell'heap per trovare perdite di memoria.
  3. Risolvi le perdite di memoria.

Per maggiori dettagli, consulta le sezioni seguenti.

Acquisisci un dump dell'heap

Per acquisire un dump dell'heap, puoi utilizzare Android Debug Bridge (adb) o Profiler di memoria di Android Studio.

Utilizza adb per acquisire un dump dell'heap

Per acquisire un dump dell'heap utilizzando adb, segui questi passaggi:

  1. Collega il dispositivo Android al computer.
  2. Apri un prompt dei comandi e vai alla directory in cui si trovano gli strumenti adb.
  3. Per acquisire un dump dell'heap, esegui questo comando:

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

  4. Per recuperare il dump dell'heap, esegui questo comando:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

Utilizzare Android Studio per acquisire un dump dell'heap

Per acquisire un dump dell'heap utilizzando il Profiler di memoria di Android Studio, segui questi passaggi passi in Android Acquisire un heapdump .

Analizza il dump dell'heap per individuare le perdite di memoria

Dopo aver acquisito un dump dell'heap, puoi utilizzare la classe di archiviazione Profiler per analizzarlo. Per farlo, segui questi passaggi:

  1. Apri il progetto Android in Android Studio.

  2. Seleziona Esegui e poi la configurazione Debug.

  3. Apri la scheda Android Profiler.

  4. Seleziona Memoria.

  5. Seleziona Apri dump dell'heap e seleziona il file di dump dell'heap che hai generato. Il profiler di memoria mostra un grafico della memoria utilizzata dalla tua app.

  6. Utilizza il grafico per analizzare il dump dell'heap:

    • Identifica gli oggetti che non vengono più utilizzati.

    • Identifica gli oggetti che utilizzano molta memoria.

    • Scopri la quantità di memoria utilizzata da ciascun oggetto.

  7. Utilizza queste informazioni per restringere o trovare l'origine della perdita di memoria e correggilo.

Passaggio 5: correggi le perdite di memoria

Dopo aver identificato l'origine della perdita di memoria, puoi correggerla. La correzione delle perdite di memoria nelle app per Android contribuisce a migliorare le prestazioni e la stabilità delle app. I dettagli variano a seconda dello scenario. Tuttavia, i seguenti suggerimenti possono essere utili:

Altri strumenti di debug

Al termine di questi passaggi, se non hai ancora trovato e corretto la perdita di memoria, prova questi strumenti:

Esegui il debug della memoria nel codice nativo con il monitoraggio dell'allocazione

Anche se non utilizzi direttamente il codice nativo, diverse librerie Android comuni lo fanno, inclusi gli SDK di Google. Se ritieni che la perdita di memoria si trovi nel codice nativo, esistono diversi strumenti che puoi utilizzare per eseguire il debug. Il monitoraggio dell'allocazione con Android Studio o heapprofd (compatibile anche con Perfetto) è un ottimo modo per identificare le potenziali cause di una perdita di memoria ed è spesso il modo più rapido per eseguire il debug.

Il monitoraggio dell'allocazione offre anche il vantaggio distinto di condividere senza includere informazioni sensibili presenti in un heap.

Identifica le fughe di notizie con LeakCanary

LeakCanary è un potente strumento per identificare le perdite di memoria nelle app per Android. Per scoprire di più su come utilizzare LeakCanary nella tua app, visita LeakCanary.

Come segnalare problemi con gli SDK Google

Se hai provato i metodi descritti in questo documento e sospetti una perdita di memoria nei nostri SDK, contatta l'assistenza clienti fornendo quante più informazioni possibili tra le seguenti:

  • Passaggi per ricreare la perdita di memoria. Se i passaggi richiedono una programmazione complessa, può essere utile copiare il codice che replica il problema nella nostra app di esempio e indica i passaggi aggiuntivi da eseguire nell'interfaccia utente per attivare perdita di dati.

  • Dump dell'heap acquisiti dalla tua app con il problema ricreato. Acquisisci dump dell'heap in due momenti diversi che mostrano che l'utilizzo della memoria è aumentato in modo significativo.

  • Se è prevista una perdita di memoria nativa, condividi l'allocazione di monitoraggio dell'output heapprofd

  • Una segnalazione di bug effettuata dopo aver ricreato la condizione di perdita.

  • Tracce dello stack di eventuali arresti anomali correlati alla memoria.

    Nota importante: in genere le analisi dello stack non sono sufficienti per eseguire il debug di un problema di memoria, quindi assicurati di specificare anche uno degli altri moduli di informazioni.