Gestisci la memoria in modo efficace nei giochi

Sulla piattaforma Android, il sistema cerca di utilizzare la maggior quantità di memoria di sistema (RAM) possibile ed esegue varie ottimizzazioni della memoria per liberare spazio quando necessario. Queste ottimizzazioni possono avere un effetto negativo sul gioco, rallentandolo o interrompendolo del tutto. Per ulteriori informazioni su queste ottimizzazioni, consulta l'argomento Assegnazione della memoria tra i processi.

In questa pagina vengono spiegati i passaggi da seguire per evitare che le condizioni di memoria insufficiente influiscano sul tuo gioco.

Rispondere a onTrimMemory()

Il sistema utilizza onTrimMemory() per notificare alla tua app che la memoria è in esaurimento e che l'app potrebbe essere interrotta. Molte volte, questo è l'unico avviso che riceve la tua app. Questo callback ha un'alta latenza relativa al killer a memoria ridotta (LMK), quindi è fondamentale rispondere rapidamente al callback.

In risposta a questo callback, riduci la velocità, il numero e le dimensioni delle allocazioni. onTrimMemory() passa una costante che indica la gravità, ma devi rispondere al primo avviso perché è possibile eseguire l'allocazione più rapidamente di quanto onTrimMemory() può reagire.

Kotlin

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
    override fun onTrimMemory(level: Int) {
        when (level) {
            ComponentCallbacks2.TRIM_MEMORY_MODERATE,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> // Respond to low memory condition
            else -> Unit
        }
    }
}

Java

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
    public void onTrimMemory(int level) {
        switch (level) {
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
              // Respond to low memory condition
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
              // Respond to low memory condition
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
              // Respond to low memory condition
                break;
            default:
                break;

C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

class LowMemoryTrigger : MonoBehaviour
{
    private void Start()
    {
        Application.lowMemory += OnLowMemory;
    }
    private void OnLowMemory()
    {
        // Respond to low memory condition (e.g., Resources.UnloadUnusedAssets())
    }
}

Usa l'API Memory Advice beta

L'API Memory Advice è stata sviluppata come alternativa a onTrimMemory, che offre richiamo e precisione molto più elevati nella previsione degli LMK imminenti. A questo scopo, l'API stima la quantità di risorse di memoria in uso e, successivamente, invia una notifica all'app quando vengono superate determinate soglie. L'API può anche segnalare la percentuale stimata di memoria utilizzata direttamente nella tua app. Puoi utilizzare l'API Memory Advice come alternativa agli eventi onTrimMemory per la gestione della memoria.

Per utilizzare l'API Memory Advice, consulta la Guida introduttiva.

Usa un approccio conservativo con i budget per la memoria

Memoria del budget in modo conservativo per evitare di esaurire la memoria. Ecco alcuni aspetti da considerare:

  • Dimensione della RAM fisica: i giochi utilizzano spesso 1⁄4-1⁄2 della quantità di RAM fisica sul dispositivo.
  • Dimensione massima zRAM: più zRAM significa che il gioco ha potenzialmente più memoria da allocare. Questo importo può variare in base al dispositivo; cerca SwapTotal in /proc/meminfo per trovare questo valore.
  • Utilizzo della memoria del sistema operativo: i dispositivi che assegnano più RAM ai processi di sistema lasciano meno memoria per il gioco. Il sistema termina il processo del gioco prima di terminare i processi di sistema.
  • Utilizzo memoria delle app installate: testa il tuo gioco su dispositivi su cui sono installate molte app. Le app di social media e chat devono essere eseguite costantemente, a incidere sulla quantità di memoria libera.

Se non puoi impegnarti con un budget conservativo per la memoria, adotta un approccio più flessibile. Se il sistema riscontra problemi di memoria insufficiente, riduci la quantità di memoria utilizzata dal gioco. Ad esempio, assegna texture a risoluzione più bassa o archivia meno mesh in risposta a onTrimMemory(). Questo approccio dinamico all'allocazione della memoria richiede più lavoro da parte dello sviluppatore, soprattutto nella fase di progettazione del gioco.

Evita il thrash

La minaccia si verifica quando la memoria disponibile è insufficiente, ma non sufficientemente bassa da poter terminare il gioco. In questo caso, kswapd ha recuperato pagine che ancora servono al gioco, quindi prova a ricaricarle dalla memoria. Spazio insufficiente, quindi le pagine continuano a essere scambiate (scambio continuo). Tracciamento del sistema segnala questa situazione come un thread in cui kswapd viene eseguito ininterrottamente.

Un sintomo del thrashing è la lunga durata frame, magari un secondo o più. Riduci l'utilizzo di memoria del gioco per risolvere questa situazione.

Utilizza gli strumenti disponibili

Android offre una serie di strumenti per aiutarti a comprendere in che modo il sistema gestisce la memoria.

Meminfo

Questo strumento raccoglie statistiche sulla memoria per mostrare la quantità di memoriaPSS allocata e le categorie per cui è stata utilizzata.

Stampa le statistiche meminfo in uno dei seguenti modi:

  • Utilizza il comando adb shell dumpsys meminfo package-name.
  • Utilizza la chiamata MemoryInfo dall'API Android Debug.

La statistica PrivateDirty mostra la quantità di RAM all'interno del processo che non può essere impaginata su disco e che non può essere condivisa con altri processi. La maggior parte di questa quantità diventa disponibile per il sistema quando il processo viene interrotto.

Tracce della memoria

I punti di traccia della memoria monitorano la quantità di memoria RSS utilizzata dal gioco. Il calcolo dell'utilizzo della memoria RSS è molto più veloce rispetto al calcolo dell'utilizzo di SSS. Grazie al calcolo più rapido, RSS mostra una granularità maggiore sulle variazioni di dimensione della memoria per misurazioni più precise dell'utilizzo di memoria di picco. Pertanto, è più facile notare picchi che potrebbero causare l'esaurimento della memoria del gioco.

Perfetto e tracce lunghe

Perfetto è una suite di strumenti per raccogliere informazioni su prestazioni e memoria su un dispositivo e visualizzarle in una UI basata sul web. Supporta tracce arbitrariamente lunghe in modo da poter visualizzare le variazioni del feed RSS nel tempo. Puoi anche eseguire query SQL sui dati prodotti per l'elaborazione offline. Attiva le tracce lunghe dall'app System Tracciamento. Assicurati che la categoria memory:Memory sia abilitata per la traccia.

Heapprofd

heapprofd è uno strumento di monitoraggio della memoria che fa parte di Perfetto. Questo strumento può aiutarti a trovare perdite di memoria mostrando dove è stata allocata la memoria utilizzando malloc. heapprofd può essere avviato utilizzando uno script Python e, poiché lo strumento ha un overhead ridotto, non influisce sulle prestazioni come altri strumenti come Malloc Debug.

segnalazione di bug

bugreport è uno strumento di logging per scoprire se il tuo gioco ha subito un arresto anomalo a causa dell'esaurimento della memoria. L'output dello strumento è molto più dettagliato rispetto all'uso di logcat. È utile per il debug della memoria perché indica se il gioco si è arrestato in modo anomalo a causa dell'esaurimento della memoria o se è stato interrotto dall'LMK.

Per maggiori informazioni, consulta Acquisire e leggere le segnalazioni di bug.