L'ottimizzazione della memoria è fondamentale per offrire esperienze di gioco stabili e ad alte prestazioni su Android. Questa guida fornisce una panoramica del motivo per cui l'efficienza della memoria è importante, di come il sistema operativo Android gestisce i limiti di memoria dei processi e delle nuove metriche della memoria in Google Play Console per aiutarti a monitorare e migliorare la qualità tecnica del tuo gioco.
L'importanza dell'ottimizzazione della memoria
L'ottimizzazione della memoria del gioco è essenziale per mantenere la fidelizzazione dei giocatori, espandere la compatibilità con i dispositivi e rispettare gli standard di qualità della piattaforma:
- Prevenzione dell'avvio a freddo (esperienza utente e fidelizzazione): quando un giocatore si allontana temporaneamente dal gioco (ad esempio per rispondere a una notifica o controllare un messaggio), il sistema operativo mette il processo di gioco in background. Se il footprint della memoria in background del gioco è troppo elevato, il Low Memory Killer (LMK) del sistema dà la priorità alla terminazione del processo del gioco per recuperare RAM per le attività in primo piano. La volta successiva che l'utente riprende, invece di un ripristino a caldo fluido e istantaneo, il gioco deve eseguire un lungo avvio a freddo, ricaricando completamente dallo spazio di archiviazione asset grafici pesanti, audio e file binari del motore grafico. Mantenere basso l'utilizzo della memoria utilizzata in background impedisce queste terminazioni silenziose in background, preservando lo stato dell'utente e garantendo che i giocatori possano riprendere immediatamente la sessione. Per maggiori dettagli sul comportamento del sistema LMK, consulta la guida Android vitals - Low memory killer.
- Stabilità dell'ecosistema e dei dispositivi: l'utilizzo inefficiente della memoria e le perdite di memoria compromettono l'integrità complessiva del sistema. Quando la memoria di sistema è scarsa, il sistema è sottoposto a una forte pressione, con conseguenti cali della frequenza fotogrammi, stuttering dell'interfaccia utente e problemi audio. Se la pressione sulla memoria è troppo elevata, il Low Memory Killer (LMK) del sistema termina in modo aggressivo i processi in background, costringendo le altre applicazioni a subire avvii a freddo lenti e la perdita dello stato utente quando i giocatori passano da un'attività all'altra.
- Terminazioni a livello di piattaforma: a partire da Android 17 (livello API 37), il sistema è più proattivo nel terminare i processi che utilizzano troppa memoria. Se l'impatto del gioco è troppo elevato, il sistema operativo può terminare il processo bruscamente senza generare un'analisi dello stack standard.
- Compatibilità dei dispositivi: mentre i dispositivi di punta dispongono di 12-16 GB di RAM, una parte enorme del pubblico globale di giocatori utilizza dispositivi con 4 o 6 GB di RAM. Una corretta gestione della memoria garantisce che il gioco rimanga accessibile e reattivo su tutti i livelli hardware senza richiedere pacchetti di asset complessi e separati.
Informazioni sulla memoria in Android
Per progettare strategie efficaci di allocazione della memoria, gli sviluppatori devono comprendere in che modo la piattaforma Android gestisce la memoria fisica e come misura l'impatto attivo del gioco.
Concetti principali della memoria Android
Per i concetti fondamentali relativi alla gestione della memoria a livello di piattaforma, consulta la documentazione ufficiale della panoramica della gestione della memoria. Questa risorsa copre quattro aree architettoniche:
- Panoramica della memoria: Android utilizza il paging e la mappatura della memoria (mmap) per gestire la RAM. Non supporta un file di swap tradizionale su disco, ma si basa sulla compressione delle pagine (utilizzando zRAM) e sul recupero delle pagine per liberare memoria fisica.
- Allocazione della memoria tra i processi: Android condivide la RAM nell'intero sistema. Assegna heap specifici per l'esecuzione della macchina virtuale Dalvik o ART, consentendo al contempo agli ambienti di sviluppo nativi (come i motori grafici C++) di richiedere memoria dall'heap di sistema nativo.
- Gestione della memoria delle app: operando in un modello multiprocesso, Android si aspetta che le applicazioni monitorino lo stato del loro ciclo di vita in modo dinamico e rilascino volontariamente le risorse non necessarie (come grafiche e bitmap non memorizzati nella cache) per supportare l'integrità del sistema.
- Panoramica di processi e thread: il sistema classifica i processi in una gerarchia in base alla loro visibilità e importanza attuali percepite dall'utente, determinando quali processi vengono mantenuti attivi e quali vengono terminati per primi in condizioni di memoria insufficiente.
La metrica Footprint della memoria totale
Il limitatore di memoria di Android 17 a livello di piattaforma valuta il consumo dei processi utilizzando il footprint della memoria totale anziché la dimensione residente totale (RSS) o la dimensione della memoria virtuale.
Footprint della memoria totale = RSS anonimo (RssAnon) + swap non compresso (VmSwap)
Per evitare che i giochi superino i limiti della piattaforma, devi capire esattamente cosa rappresentano queste metriche a livello di sistema. Per ulteriori informazioni su queste metriche, sulle allocazioni della RAM fisica e su come vengono gestite le pagine con backup su file, consulta Informazioni sulle metriche RSS e di swap nella guida Monitora l'utilizzo della memoria.
Vincoli di memoria
Per mantenere la stabilità del sistema e garantire che le applicazioni non consumino risorse eccessive, la piattaforma Android gestisce i limiti di memoria per i processi in esecuzione.
Limitatore di memoria in Android 17 e versioni successive
Android 17 (livello API 37) e versioni successive gestiscono limiti di memoria rigorosi per app utilizzando Linux cgroup v2 per evitare che singole app causino instabilità a livello di sistema. Per maggiori dettagli sull'implementazione tecnica, consulta la guida al limitatore di memoria AOSP e il blog L'efficienza della memoria al primo posto: passaggi essenziali per Android 17.
- Meccanismo: il limitatore di memoria monitora tutti i processi dell'applicazione e assegna dinamicamente i limiti in base allo stato del ciclo di vita del processo:
- Processi visibili (in primo piano): i processi delle app che attualmente mostrano una UI dovrebbero eseguire un working set di risorse più ampio e hanno un limite più generoso.
- Processi non visibili (background o servizi): i processi delle app che svolgono un lavoro attivo senza mostrare un'UI sono limitati a un budget più ristretto e restrittivo.
- Attributi del kernel: il servizio si basa su due attributi principali:
memory.high: un limite flessibile. Se questo limite viene superato, il kernel limita il processo e tenta di recuperare la memoria in modo aggressivo. Questo recupero può causare un peggioramento delle prestazioni del gioco.memory.swap.max: gestisce un limite massimo per lo spazio di swap o zRAM che il processo può utilizzare.
- Comportamento di terminazione: se un processo continua ad allocare memoria anonima oltre
memory.highed esaurisce la capacità di swap, le allocazioni non vanno a buon fine e il sistema operativo termina il processo in modo silenzioso. Questa terminazione viene registrata utilizzandoApplicationExitInfocon il motivo di uscita Limitatore di memoria (disponibile a partire da Android 17, 26Q4).
Nuovi limiti di memoria in Android vitals di Play Console
Per aiutare gli sviluppatori a identificare in modo proattivo i problemi di memoria, Google Play sta introducendo nuove metriche in Android vitals di Play Console. Play Console monitora il footprint della memoria RSS anonimo + swap del 90° percentile (P90) delle sessioni di gioco per identificare i valori anomali estremi.
Le soglie di avviso e di applicazione vengono scalate in base alla capacità della RAM fisica del dispositivo e agli stati dei processi. Questi limiti si applicano in due fasi distinte.
Per linee guida dettagliate e limiti di memoria, consulta la sezione Android vitals - "Quali sono le soglie relative alle prestazioni scadenti?".
Servizi percepiti dall'utente
I servizi percepibili sono processi in background critici che il sistema Android considera visibili all'utente. Questo stato comprende tutti i processi in esecuzione:
- Servizi in primo piano (FGS)
- Job rapidi
- User-initiated data transfer jobs
- Servizi associati al sistema o servizi associati da altre applicazioni
Poiché i servizi percepibili sono progettati per attività critiche e di lunga durata in background, sono altamente suscettibili a leak cumulativi del ciclo di vita. I limiti di memoria della piattaforma Android trattano questo stato come non in primo piano, il che significa che se il tuo gioco è in background, ma continua a eseguire un servizio percepibile, è soggetto ai limiti di memoria in background o del servizio più restrittivi mostrati nella pagina del Centro assistenza Play. I giochi devono ridurre in modo aggressivo gli asset non necessari quando passano dal primo piano a uno stato di servizio percepibile in background.
Requisiti di R8
Per ridurre al minimo le dimensioni del bytecode e l'overhead del processo Java di base, Google Play Console valuta l'ottimizzazione del codice nell'ambito delle sue norme sulla qualità delle app. Per maggiori informazioni sui requisiti e sui limiti di ottimizzazione applicabili ai giochi, consulta Ottimizzazione del codice DEX nella documentazione di Android vitals.
Per configurare R8 in un progetto Android standard, segui la guida Abilita l'ottimizzazione delle app. Per attivare le impostazioni avanzate di riduzione e ottimizzazione, consulta Utilizza R8 in modalità completa. Per identificare le regole che impediscono a R8 di offuscare le classi o rimuovere il codice non utilizzato, utilizza R8 Configuration Analyzer.
Se utilizzi un motore grafico, il comportamento predefinito di R8 varia in base al motore e alla versione:
- Unity: la minimizzazione del codice è disattivata per impostazione predefinita, quindi devi attivarla nelle impostazioni del progetto. Per ulteriori informazioni sul supporto delle versioni e sui passaggi di configurazione, consulta Riduci e ottimizza il codice Unity con R8.
- Unreal Engine: a partire da UE 4.25, R8 è abilitato per impostazione predefinita nelle build di release, il che richiede la gestione delle regole di conservazione per i bridge e i plug-in JNI. Per maggiori informazioni sui dettagli della versione e sulla configurazione UPL, consulta Riduci e ottimizza il codice di Unreal Engine con R8.
Requisiti delle bitmap
Le bitmap rappresentano una parte enorme della memoria utilizzata nei moderni giochi ad alta fedeltà. Poiché i dati dei pixel della bitmap vengono archiviati direttamente nell'heap nativo non gestito su Android 8.0 (livello API 26) e versioni successive, il caricamento di immagini non ottimizzate può spingere i processi oltre le soglie di memoria della piattaforma. Per le best practice sulla scalabilità e sulla memorizzazione nella cache delle immagini, consulta Ottimizza l'utilizzo delle immagini.
Monitora la memoria utilizzata
Per ottimizzare in modo efficace la memoria del gioco, devi prima capire in che modo la piattaforma Android ne misura l'impatto. Android 17 aggiorna la metrica della memoria per monitorare la somma di RSS anonimo (RssAnon) e swap non compresso (VmSwap), esclusa la memoria con backup su file o privata della GPU. Questa guida spiega
come utilizzare strumenti a livello di sistema come Perfetto e meminfo, implementare
API di diagnostica come ProfilingManager e onTrimMemory ed estrarre
allocazioni di memoria precise in Unity e Unreal Engine. Scopri come profilare con precisione il tuo gioco ed evitare i problemi di prestazioni associati al tradizionale polling della memoria di runtime.
Per saperne di più, consulta Monitora l'utilizzo della memoria.
Strategie di riduzione della memoria
Sebbene i motori grafici semplifichino lo sviluppo multipiattaforma, la gestione predefinita della memoria può attivare limiti di memoria a livello di sistema operativo. Questa pagina descrive i passaggi pratici di ottimizzazione specifici per Unity e Unreal Engine. Scopri perché l'utilizzo di onTrimMemory basati su Java può causare deadlock in Unity e come utilizzare i callback del ciclo di vita nativi. Scoprirai anche le principali ottimizzazioni a livello di asset, come l'utilizzo della compressione delle texture ASTC 8x8 e la configurazione degli scaricamenti degli asset, per mantenere il gioco fluido su tutti i livelli hardware.
Per saperne di più, consulta Riduci la memoria utilizzata.