Limitatore di memoria

Android 17 e versioni successive supportano Memory Limiter, un servizio di sistema che monitora e limita la memoria utilizzata dei processi delle applicazioni utilizzando Linux cgroup v2. Memory Limiter impedisce alle singole app di consumare una quantità eccessiva di memoria di sistema, il che riduce la pressione della memoria a livello di sistema e impedisce l'eliminazione aggressiva dei processi critici per esaurimento della memoria (OOM).

Meccanismo

Memory Limiter si integra con Activity Manager Service (AMS) per monitorare gli eventi del ciclo di vita dei processi e le modifiche dello stato. Memory Limiter applica i limiti di memoria utilizzando il file system cgroup v2 del kernel Linux.

Per utilizzare Memory Limiter, il kernel del dispositivo deve supportare cgroup v2 e il controller memory. Il servizio si basa in particolare sui seguenti attributi:

memory.high
Un limite flessibile. Quando viene superato, il processo viene limitato e il kernel tenta di recuperare la memoria.
memory.swap.max
Limita la quantità di spazio di swap che il processo può utilizzare.

Impatto sulle app

Le app che non superano i limiti di memoria non sono interessate da Memory Limiter.

Quando un'app supera il limite memory.high, il kernel espelle la memoria supportata da file dell'app e scambia la memoria anonima per mantenerla entro il limite. A causa dell'espulsione e dello scambio, l'app potrebbe essere eseguita più lentamente.

In casi estremi, se l'app continua ad allocare memoria anonima e il dispositivo esaurisce lo spazio di swap, l'app potrebbe non riuscire ad allocare memoria e, di conseguenza, è probabile che si arresti in modo anomalo.

Monitoraggio dei processi

Per impostazione predefinita, Memory Limiter monitora i processi delle app (UID >= 10000). I processi di sistema sono generalmente esenti per contribuire a verificare la stabilità del sistema di base.

Memory Limiter assegna i limiti di memoria in base allo stato del processo:

  • I processi visibili sono in uno stato in cui possono mostrare un'interfaccia utente all'utente. Quando viene visualizzata un'interfaccia utente, è possibile che un processo utilizzi un set di lavoro RAM più grande, pertanto viene assegnato un limite di memoria più generoso.

  • I processi non visibili sono in uno stato in cui svolgono attivamente il lavoro, ma non disegnano un'interfaccia utente. Utilizzano la memoria per eseguire questo lavoro, ma meno di quanto necessario quando mostrano un'interfaccia utente, quindi viene assegnato un limite più restrittivo.

La tabella seguente associa gli stati dei processi specifici ai limiti di memoria:

Stato elaborazioneLimite di memoria
PERSISTENTSenza restrizioni
PERSISTENT_UISenza restrizioni
TOPVisibile
BOUND_TOPVisibile
FOREGROUND_SERVICENon visibile
BOUND_FOREGROUND_SERVICENon visibile
IMPORTANT_FOREGROUNDVisibile
IMPORTANT_BACKGROUNDNon visibile
TRANSIENT_BACKGROUNDNon visibile
BACKUPNon visibile
SERVICENon visibile
RECEIVERNon visibile
TOP_SLEEPINGVisibile
HEAVY_WEIGHTNon visibile
HOMENon visibile
LAST_ACTIVITYNon visibile
CACHED_ACTIVITYMemorizzata nella cache
CACHED_ACTIVITY_CLIENTMemorizzata nella cache
CACHED_RECENTMemorizzata nella cache
CACHED_EMPTYMemorizzata nella cache

Nello stato memorizzato nella cache, i processi vengono bloccati e poi recuperati al massimo.

Quando un processo supera il limite memory.high assegnato, Memory Limiter rileva l'evento e può attivare azioni di debug, ad esempio l'acquisizione di un profilo di memoria o la registrazione di un'anomalia in statsd.

Configurazione

Configura Memory Limiter utilizzando un file XML che si trova nella partizione vendor. La configurazione consente di ottimizzare i limiti di memoria assoluti in base ai vincoli di memoria specifici del dispositivo.

  • Percorso file: /vendor/etc/memory-limiter-config.xml

  • Configurazione predefinita: se il file di configurazione non viene trovato o se non è leggibile o valido, Memory Limiter viene disattivato.

XML

Il file di configurazione segue lo schema definito in memory-limiter-config.xsd. Il file consente di definire più set di limiti; il servizio sceglie la corrispondenza migliore in base alla RAM disponibile del dispositivo. Tutti i valori di memoria sono definiti in unità di mebibyte (MiB).

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <limitSet>
      <!-- Limits for a phone with at least 14G of ram: 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
Un numero intero positivo che identifica la versione di configurazione. Deve essere 1.
minimumRequiredMemTotal
La memoria di sistema disponibile minima richiesta per la validità di questo set di limiti.
memVisible
Il limite di memoria (memory.high) consentito per i processi visibili.
memNotVisible
Il limite di memoria (memory.high) consentito per i processi non visibili.
swapVisible
Il limite di swap (memory.swap.max) consentito per i processi visibili.
swapNotVisible
Il limite di swap (memory.swap.max) consentito per i processi non visibili.

Linee guida per il limite di memoria del dispositivo

Quando configuri i limiti di memoria per il dispositivo, tieni presente le seguenti linee guida:

  • Adatta i limiti alle funzionalità hardware: gli OEM dei dispositivi possono impostare limiti personalizzati in base alle funzionalità hardware del dispositivo. Android consiglia i seguenti intervalli:

    • Processi visibili: almeno 1/2 e al massimo 2/3 della RAM fisica totale.
    • Processi non visibili: da 1/4 a 1/3 della RAM fisica totale. Gli OEM possono effettuare determinazioni diverse in base alle funzionalità e ai casi d'uso del dispositivo.
  • Nessuna API di runtime per le app: a partire da Android 17 (SDK 37), le app non hanno un'API per eseguire query sui limiti di memoria in fase di runtime. Gli OEM devono tenerne conto ed evitare di impostare limiti troppo bassi, assicurandosi che le app non raggiungano i limiti durante i casi d'uso ragionevoli.

  • Configurazione universale: i limiti si applicano a tutti i processi delle app sul dispositivo, incluse le app preinstallate. Non esiste una lista consentita per esentare determinate app da questi limiti.

Modifica della configurazione

Per modificare i limiti a livello di sistema:

  1. Modifica /vendor/etc/memory-limiter-config.xml.
  2. Riavvia il dispositivo o riavvia system_server per applicare le modifiche.

Comandi shell

Il comando am memory-limiter consente a te e agli sviluppatori di interagire con il servizio in fase di runtime per lo sviluppo e i test:

am memory-limiter <SUB-COMMAND>

stato

Il sottocomando status segnala lo stato operativo di Memory Limiter:

adb shell am memory-limiter status

Output di esempio:

Memory limiter
  enabled                  monitoring=true          ignored=none
  visibleMem=1948MB        visibleSwap=974MB
  notVisibleMem=974MB      notVisibleSwap=487MB
  started=36               watched=36               watch-failed=0
  events=0                 processes=36             process-hwm=36

I campi chiave nell'output includono:

monitoring
Indica se il limitatore sta monitorando attivamente i processi.
visibleMem e notVisibleMem
Indicano i limiti di memoria assoluti calcolati per ogni stato.
events
Il numero di volte in cui un processo ha superato il limite.
processes
Il numero di processi monitorati.

ignora

Il sottocomando ignore esclude temporaneamente un UID o tutti i processi dalla limitazione. Questa azione è utile per i test delle prestazioni o quando si consente a un'app specifica di superare i limiti.

adb shell am memory-limiter ignore 10087  // Ignore a specific UID
adb shell am memory-limiter ignore all    // Ignore all processes (effectively disables limiting)
adb shell am memory-limiter ignore none   // Resume normal operation

manuale

Il sottocomando manual esegue l'override dei limiti calcolati per un processo specifico (in base all'ID processo o PID) con un valore assoluto personalizzato in megabyte (MB):

adb shell am memory-limiter manual 1234 1024   // Set a 1024 MB limit for PID 1234
adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234

Gli override manuali si applicano solo al ciclo di vita del processo. Se il processo viene riavviato, torna ai limiti predefiniti in base al suo stato.