Gestire la memoria in modo efficace nei giochi

Sulla piattaforma Android, il sistema tenta di utilizzare la maggior parte possibile della memoria di sistema (RAM) ed esegue varie ottimizzazioni della memoria per liberare spazio in caso di necessità. Queste ottimizzazioni possono avere un effetto negativo sul tuo gioco, rallentandolo o interrompendolo del tutto. Puoi scoprire di più su queste ottimizzazioni nell'argomento Allocazione della memoria tra i processi.

Questa pagina spiega i passaggi da seguire per evitare che condizioni di scarsa memoria influiscano sul tuo gioco.

Rispondere a onTrimMemory()

Il sistema utilizza onTrimMemory() per notificare all'app gli eventi del ciclo di vita che rappresentano un'ottima opportunità per ridurre volontariamente l'utilizzo della memoria ed evitare di essere uccisa dal task killer per memoria insufficiente (LMK) per liberare memoria da utilizzare per altre app.

Se la tua app viene interrotta in background, la volta successiva che l'utente la avvia, l'avvio a freddo sarà lento. Le app che riducono il loro utilizzo di memoria quando passano in background hanno meno probabilità di essere interrotte in background.

Quando rispondi agli eventi di taglio, è meglio rilasciare allocazioni di memoria di grandi dimensioni che non sono necessarie immediatamente e che potrebbero essere ricostruite su richiesta. Ad esempio, se la tua app ha una cache di bitmap decodificate da immagini compresse memorizzate localmente, spesso è buona prassi rimuovere o eliminare questa cache in risposta a TRIM_MEMORY_UI_HIDDEN.

Kotlin

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
    override fun onTrimMemory(level: Int) {
        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Release memory related to UI elements, such as bitmap caches.
        }
        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Release memory related to background processing, such as by
            // closing a database connection.
        }
    }
}

Java

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
    public void onTrimMemory(int level) {
        switch (level) {
            if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
                // Release memory related to UI elements, such as bitmap caches.
            }
            if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
                // Release memory related to background processing, such as by
                // closing a database connection.
            }
        }
    }
}

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())
    }
}

Utilizzare la versione beta dell'API Memory Advice

L'API Memory Advice è stata sviluppata come alternativa a onTrimMemory e offre un richiamo e una precisione molto più elevati nella previsione di LMK imminenti. L'API ottiene questo risultato stimando la quantità di risorse di memoria in uso e poi inviando una notifica all'app quando vengono superate determinate soglie. L'API può anche segnalare la percentuale stimata di utilizzo della memoria direttamente alla 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.

Rispetta i budget di memoria

Pianifica la memoria in modo conservativo per evitare di esaurirla. Ecco alcuni aspetti da prendere in considerazione:

  • Dimensioni della RAM fisica: i giochi utilizzano spesso tra ¼ e ½ della quantità di RAM fisica sul dispositivo.
  • Dimensioni massime della zRAM: più zRAM significa che il gioco ha potenzialmente più memoria da allocare. Questo importo può variare in base al dispositivo. Per trovarlo, cerca SwapTotal in /proc/meminfo.
  • 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 della 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 in esecuzione costante e influiscono sulla quantità di memoria libera.

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

Evitare il thrashing

Il thrashing si verifica quando la memoria libera è bassa, ma non abbastanza da arrestare il gioco. In questa situazione, kswapd ha recuperato pagine di cui il gioco ha ancora bisogno, quindi versucht di ricaricarle dalla memoria. Non c'è spazio sufficiente, quindi le pagine continuano a essere sostituite (scambio continuo). Il monitoraggio del sistema registra questa situazione come un thread in cui kswapd viene eseguito continuamente.

Un sintomo di thrashing è il tempo di frame lungo, eventualmente un secondo o più. Riduci l'utilizzo di memoria del gioco per risolvere la situazione.

Utilizzare gli strumenti disponibili

Android dispone di una raccolta di strumenti utili per comprendere come il sistema gestisce la memoria.

Meminfo

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

Stampa le statistiche di meminfo in uno dei seguenti modi:

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

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

Punto di traccia della memoria

I tracepoint della memoria monitorano la quantità di memoria RSS utilizzata dal gioco. Il calcolo dell'utilizzo della memoria RSS è molto più rapido rispetto al calcolo dell'utilizzo della memoria PSS. Poiché è più veloce da calcolare, l'RSS mostra una granularità più fine delle variazioni delle dimensioni della memoria per misurazioni più accurate dell'utilizzo massimo della memoria. Di conseguenza, è più facile notare i picchi che potrebbero causare un esaurimento della memoria del gioco.

Tracce perfette e lunghe

Perfetto è una suite di strumenti per raccogliere informazioni su prestazioni e memoria su un dispositivo e visualizzarle in un'interfaccia utente basata sul web. Supporta tracce arbitrariamente lunghe, in modo da poter visualizzare l'evoluzione dell'RSS nel tempo. Puoi anche eseguire query SQL sui dati prodotti per l'elaborazione offline. Attiva le tracce lunghe dall'app Monitoraggio sistema. Assicurati che la categoria memory:Memory sia attivata 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.

bugreport

bugreport è uno strumento di registrazione per scoprire se il gioco ha avuto un arresto anomalo o meno perché ha esaurito la memoria. L'output dello strumento è molto più dettagliato rispetto all'utilizzo di logcat. È utile per il debug della memoria perché mostra se il gioco ha avuto un arresto anomalo perché ha esaurito la memoria o se è stato interrotto dall'LMK.

Per ulteriori informazioni, consulta Acquisire e leggere i report di bug.