Questo documento presuppone che tu abbia seguito le indicazioni delle best practice per le app per Android in Gestione della memoria, ad esempio Gestire la memoria dell'app.
Introduzione
Una perdita di memoria è un tipo di perdita di risorse che si verifica quando un programma per computer non rilascia la memoria allocata che non è più necessaria. Una perdita può portare l'applicazione a richiedere al sistema operativo più memoria di quella disponibile, causando quindi l'arresto anomalo dell'applicazione. Una serie di pratiche improprie possono causare perdite di memoria nelle app per Android, ad esempio lo smaltimento non corretto delle risorse o l'annullamento della 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 tuo codice. Se hai provato i metodi descritti in questo documento e sospetti una perdita di memoria nei nostri SDK, consulta Come segnalare problemi relativi agli SDK di Google.
Prima di contattare l'assistenza
Prima di segnalare una perdita di memoria al team di assistenza Google, segui le best practice e i passaggi di debug forniti in questo documento per assicurarti che l'errore non si trovi nel tuo codice. Questi passaggi potrebbero risolvere il problema e, in caso contrario, generano le informazioni necessarie al team di assistenza Google per aiutarti.
Evitare perdite di memoria
Segui queste best practice per evitare alcune delle cause più comuni di perdite di memoria nel codice che utilizza gli SDK di Google.
Best practice per le app per Android
Verifica di aver eseguito tutte le seguenti operazioni nella tua applicazione Android:
- Rilascia le risorse inutilizzate.
- Annulla la registrazione dei listener quando non sono più necessari.
- Annullare le attività quando non sono necessarie.
- Inoltra i metodi del ciclo di vita per rilasciare le risorse.
- Utilizzare le versioni più recenti degli SDK
Per dettagli specifici su ciascuna di queste pratiche, consulta le sezioni seguenti.
Svincola risorse non utilizzate
Quando la tua app per Android utilizza una risorsa, assicurati di rilasciarla quando non è più necessaria. In caso contrario, la risorsa continua a occupare memoria anche dopo che l'applicazione ha terminato di utilizzarla. Per saperne di più, consulta Il ciclo di vita dell'attività nella documentazione di Android.
Rilasciare riferimenti GoogleMap obsoleti nei GeoSDK
Un errore comune è che una GoogleMap può causare una perdita di memoria se memorizzata nella cache utilizzando NavigationView o MapView. Un oggetto GoogleMap ha una relazione 1:1 con NavigationView o MapView da cui viene recuperato. Devi assicurarti che una GoogleMap non venga memorizzata nella cache o che il riferimento venga rilasciato quando viene chiamato NavigationView#onDestroy o MapView#onDestroy. Se utilizzi NavigationSupportFragment, MapSupportFragment o il tuo frammento che racchiude queste visualizzazioni, il riferimento deve essere rilasciato in 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 clic su un pulsante o una modifica dello stato di una visualizzazione, assicurati di annullare la registrazione del listener quando l'applicazione non deve più monitorare l'evento. In caso contrario, i listener continuano a occupare memoria anche dopo che l'applicazione ha terminato di utilizzarli.
Ad esempio, supponiamo che la tua applicazione utilizzi Navigation SDK e chiami
il seguente listener per ascoltare gli eventi di arrivo:
addArrivalListener
per ascoltare gli eventi di arrivo, deve chiamare anche
removeArrivalListener
quando non ha più bisogno di 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 annullare l'attività al termine. Se l'attività non viene annullata, continua a essere eseguita in background anche dopo che l'app ha terminato.
Per maggiori dettagli sulle best practice, consulta Gestire la memoria dell'app nella documentazione di Android.
Inoltra i metodi del ciclo di vita per rilasciare le risorse
Se la tua app utilizza l'SDK Navigation o Maps, assicurati di rilasciare le risorse 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é utilizzare direttamente NavigationView
e MapView
,
rispettivamente. I frammenti di supporto gestiscono l'inoltro dei metodi del 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 di Google vengono costantemente aggiornati con nuove funzionalità, correzioni di bug e miglioramenti delle prestazioni. Mantieni aggiornati gli SDK nella tua app per ricevere queste correzioni.
Eseguire il debug delle perdite di memoria
Se continui a riscontrare perdite di memoria dopo aver implementato tutti i suggerimenti applicabili in precedenza in questo documento, segui questa procedura per il debug.
Prima di iniziare, dovresti avere familiarità con il modo in cui Android gestisce la memoria. Per informazioni, leggi la panoramica della gestione della memoria di Android.
Per eseguire il debug delle perdite di memoria, segui questa procedura:
- Riproduci il problema. Questo passaggio è essenziale per il debug.
- Controlla se l'utilizzo della memoria è previsto. Verifica che l'aumento dell'utilizzo che sembra essere una perdita non sia in realtà la memoria necessaria per eseguire l'applicazione.
- Esegui il debug a un livello generale. Esistono diverse utilità che puoi utilizzare per il debug. Tre diversi set di strumenti standard aiutano a eseguire il debug dei problemi di memoria in Android: Android Studio, Perfetto e le utilità della riga di comando di Android Debug Bridge (adb).
- Controlla l'utilizzo della memoria dell'app. Ottieni un dump dell'heap e il monitoraggio dell'allocazione, quindi analizzalo.
- Correggi le perdite di memoria.
Le sezioni seguenti descrivono in dettaglio questi passaggi.
Passaggio 1: riproduci il problema
Se non sei riuscito a ricreare il problema, considera innanzitutto gli scenari che potrebbero causare la perdita di memoria. Passare direttamente all'analisi di un dump dell'heap potrebbe funzionare se sai che il problema è stato ricreato. Tuttavia, se ottieni un dump dell'heap all'avvio dell'app o in un altro momento casuale, potresti non aver attivato le condizioni per attivare una perdita. Prendi in considerazione di provare diversi scenari quando tenti di ricreare il problema:
Quale insieme di funzionalità è attivato?
Quale sequenza specifica di azioni dell'utente attiva la perdita?
- Hai provato più volte ad attivare questa sequenza?
Quali stati del ciclo di vita ha attraversato l'app?
- Hai provato più iterazioni in diversi stati del ciclo di vita?
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 di una perdita di memoria. Ad esempio, per diverse funzionalità o attività utente, considera le seguenti possibilità:
Probabilmente una perdita: l'attivazione dello scenario tramite più iterazioni comporta un aumento dell'utilizzo della memoria nel tempo.
Utilizzo della memoria probabilmente previsto: la memoria viene recuperata dopo l'interruzione dello scenario.
Utilizzo della memoria probabilmente previsto: l'utilizzo della memoria aumenta per un periodo di tempo, poi diminuisce. Ciò potrebbe essere dovuto a una cache limitata o a un altro utilizzo della memoria previsto.
Se il comportamento dell'app è probabilmente l'utilizzo di memoria previsto, il problema può essere risolto gestendo la memoria dell'app. Per assistenza, vedi Gestire la memoria dell'app.
Passaggio 3: esegui il debug a un livello elevato
Quando esegui il debug di una perdita di memoria, inizia da un livello elevato e poi fai il drill-down una volta ristrette le possibilità. Utilizza uno di questi strumenti di debug di alto livello per analizzare innanzitutto se si verifica una perdita nel tempo:
Profiler di memoria di Android Studio (consigliato)
Memory Profiler di Android Studio
Questo strumento mostra un istogramma visivo della memoria utilizzata. I dump dell'heap e il monitoraggio dell'allocazione possono essere attivati anche dalla stessa interfaccia. Questo strumento è il consiglio predefinito. Per saperne di più, consulta la pagina Profiler di memoria di Android Studio.
Perfetto Memory Counters
Perfetto ti offre un controllo preciso sul monitoraggio di diverse metriche e le presenta tutte in un unico istogramma. Per maggiori informazioni, consulta Perfetto Memory Counters.
Utilità a riga di comando Android Debug Bridge (ADB)
Gran parte di ciò che puoi monitorare con Perfetto è disponibile anche come utilità della riga di comando adb
che puoi interrogare direttamente. Ecco alcuni esempi
importanti:
Meminfo ti consente di visualizzare informazioni dettagliate sulla memoria in un determinato momento.
Procstats fornisce alcune importanti statistiche aggregate nel tempo.
Una statistica fondamentale da esaminare è l'impronta massima della memoria fisica
(maxRSS) richiesta dall'app nel tempo. MaxPSS potrebbe non essere altrettanto preciso. Per
un modo per aumentare la precisione, vedi 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 e se non è stata liberata. Questo passaggio è particolarmente utile per rilevare perdite nel codice nativo. Poiché questo strumento identifica lo stack trace, può essere un ottimo mezzo per eseguire rapidamente il debug della causa principale o per capire come ricreare il problema. Per i passaggi 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 è ottenere un dump dell'heap della tua app e poi esaminarlo per individuare eventuali perdite. Un dump dell'heap è uno snapshot di tutti gli oggetti nella memoria di un'app. Può essere utilizzato per diagnosticare perdite di memoria e altri problemi correlati alla memoria.
Android Studio può rilevare perdite di memoria non correggibili dal GC. Quando acquisisci un dump dell'heap, Android Studio controlla se è presente un'attività o un fragment ancora raggiungibile ma già distrutto.
- Acquisire un dump dell'heap.
- Analizza il dump dell'heap per trovare perdite di memoria.
- Correggere le perdite di memoria.
Per maggiori dettagli, consulta le sezioni seguenti.
Acquisire un dump dell'heap
Per acquisire un dump dell'heap, puoi utilizzare Android Debug Bridge (adb) o Android Studio Memory Profiler.
Utilizza adb per acquisire un dump dell'heap
Per acquisire un dump dell'heap utilizzando adb, segui questi passaggi:
- Collega il dispositivo Android al computer.
- Apri un prompt dei comandi e vai alla directory in cui si trovano gli strumenti adb.
Per acquisire un dump dell'heap, esegui questo comando :
adb shell am dumpheap my.app.name $PHONE_FILE_OUT
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 Android Studio Memory Profiler, segui questi passaggi nella sezione Acquisire un dump dell'heap.
Analizza il dump dell'heap per trovare perdite di memoria
Una volta acquisito un dump dell'heap, puoi utilizzare il Profiler di memoria di Android Studio per analizzarlo. Per farlo, segui questi passaggi:
Apri il progetto Android in Android Studio.
Seleziona Esegui e poi la configurazione Debug.
Apri la scheda Android Profiler.
Seleziona Memoria.
Seleziona Apri dump dell'heap e seleziona il file di dump dell'heap che hai generato. Il Profiler di memoria mostra un grafico dell'utilizzo della memoria dell'app.
Utilizza il grafico per analizzare il dump dell'heap:
Identifica gli oggetti che non vengono più utilizzati.
Identifica gli oggetti che utilizzano molta memoria.
Visualizza la quantità di memoria utilizzata da ogni oggetto.
Utilizza queste informazioni per restringere il campo o trovare l'origine della perdita di memoria e risolverla.
Passaggio 5: correggi le perdite di memoria
Una volta identificata l'origine della perdita di memoria, puoi risolverla. La correzione delle perdite di memoria nelle app per Android contribuisce a migliorare le prestazioni e la stabilità delle app. A seconda dello scenario, i dettagli variano. Tuttavia, i seguenti suggerimenti possono esserti utili:
Assicurati che la tua app allochi e deallochi la memoria come consigliato nell'argomento Android Gestire la memoria dell'app.
Rimuovi il codice o le risorse inutilizzati dalla tua app. Per informazioni dettagliate sulle app per Android, vedi Best practice per le app per Android.
Altri strumenti di debug
Una volta completati 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.
- Identificare le perdite di memoria con LeakCanary.
Eseguire 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 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 presenta anche il vantaggio distinto di consentirti di condividere i risultati senza includere informazioni sensibili che possono essere trovate in un heap.
Identificare le perdite di memoria 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 la pagina LeakCanary.
Come segnalare problemi relativi agli SDK di 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 codifica complessa, potrebbe essere utile copiare il codice che replica il problema nella nostra app di esempio e fornire passaggi aggiuntivi da eseguire nell'interfaccia utente per attivare la perdita.
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 sostanziale.
Se è prevista una perdita di memoria nativa, condividi l'output di monitoraggio dell'allocazione da heapprofd.
Una segnalazione di bug effettuata dopo aver ricreato la condizione di perdita.
Analisi dello stack di eventuali arresti anomali correlati alla memoria.
Nota importante: le analisi dello stack di solito non sono sufficienti per eseguire il debug di un problema di memoria, quindi assicurati di fornire anche uno degli altri tipi di informazioni.