Limitatore di memoria

Android 17 e versioni successive includono 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à sproporzionata di memoria di sistema, il che riduce la pressione complessiva sulla memoria e impedisce l'eliminazione aggressiva per memoria insufficiente dei processi in primo piano e memorizzati nella cache critici.

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 la gerarchia cgroup v2 del kernel Linux.

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

memory.high
Un limite flessibile. Quando viene superato, il processo viene limitato e il kernel tenta di recuperare in modo proattivo le pagine da quel processo.
memory.swap.max
Limita la quantità massima di spazio di swap (ad esempio ZRAM) che il processo può utilizzare.

Impatto sulle app

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

Quando un'app supera il limite memory.high, il kernel elimina la memoria di backup dei file puliti dell'app e scambia la memoria anonima inattiva per mantenere l'app entro il limite. A seguito di questa attività di eliminazione e swap delle pagine, l'app potrebbe subire una limitazione temporanea dell'esecuzione, ma continua a essere eseguita.

In situazioni estreme, ad esempio una perdita di memoria incontrollata in cui l'app continua ad allocare memoria anonima dopo che lo spazio di swap è esaurito, l'app non riesce ad allocare memoria e viene terminata.

Monitoraggio dei processi

Per impostazione predefinita, Memory Limiter monitora i processi delle app (UID >= 10000). I processi di sistema principali sono esenti per preservare la stabilità essenziale del sistema.

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

  • Processi visibili: processi che ospitano un'interfaccia utente visibile all'utente (ad esempio l'attività attualmente in primo piano o un'interazione attiva). Poiché la visualizzazione di un'interfaccia utente richiede un set di lavoro più grande per il rendering e le pipeline grafiche, i processi visibili ricevono un limite di memoria più generoso.

  • Processi non visibili: processi che eseguono il lavoro in background senza presentare un'interfaccia utente (ad esempio servizi in background o ricevitori di trasmissione). Poiché non eseguono il rendering dei componenti dell'interfaccia utente, operano con un limite più restrittivo.

La tabella seguente associa stati di processo specifici alle classificazioni dei 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 diagnostica, ad esempio l'acquisizione di un profilo di memoria o la registrazione di un'anomalia in statsd.

Configurazione

In Android 17 e versioni successive, Memory Limiter fornisce un file di configurazione predefinito della piattaforma standard nella partizione di sistema, supportando al contempo le sostituzioni facoltative specifiche del dispositivo nella partizione vendor:

  • Configurazione predefinita del sistema: /system/etc/memory-limiter-config.xml
  • Sostituzione del fornitore (facoltativa): /vendor/etc/memory-limiter-config.xml

Memory Limiter controlla prima /vendor/etc/memory-limiter-config.xml. Se presente, utilizza la configurazione del fornitore; in caso contrario, torna alla configurazione di sistema predefinita in /system/etc/memory-limiter-config.xml.

Formato XML

Il file di configurazione segue lo schema definito in memory-limiter-config.xsd. Il file definisce più set di limiti ordinati in base alla memoria disponibile; il servizio seleziona il set di limiti corrispondente più alto in base alla RAM disponibile del dispositivo. Tutti i valori di memoria sono specificati in mebibyte (MiB).

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <!-- RAM minimums account for MemTotal excluding carve-outs. -->
    <limitSet>
      <!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
      <minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
      <memVisible>10240</memVisible>
      <memNotVisible>5120</memNotVisible>
      <swapVisible>5120</swapVisible>
      <swapNotVisible>5120</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
      <minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
      <memVisible>5120</memVisible>
      <memNotVisible>3072</memNotVisible>
      <swapVisible>3072</swapVisible>
      <swapNotVisible>3072</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
      <minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
      <memVisible>4096</memVisible>
      <memNotVisible>2048</memNotVisible>
      <swapVisible>2048</swapVisible>
      <swapNotVisible>2048</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
      <minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
      <memVisible>2048</memVisible>
      <memNotVisible>1024</memNotVisible>
      <swapVisible>1024</swapVisible>
      <swapNotVisible>1024</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
Un numero intero positivo che identifica la versione della configurazione. Deve essere 1.
minimumRequiredMemTotal

Il valore MemTotal minimo del sistema (in MiB) richiesto per l'applicazione di questo set di limiti. Il servizio valuta questo valore rispetto alla memoria totale disponibile segnalata dal kernel in /proc/meminfo.

Informazioni sulle riserve di memoria: il valore MemTotal del kernel riflette la DRAM fisica accessibile al sistema operativo dopo la detrazione delle riserve di memoria hardware e firmware. Le riserve riservano la RAM per componenti hardware specializzati come GPU, modem della banda base, ISP della fotocamera, ambienti di esecuzione sicuri e hypervisor, in genere consumando tra 500 MiB e oltre 1 GiB di memoria fisica.

Poiché MemTotal è inferiore alla DRAM fisica pubblicizzata del dispositivo, minimumRequiredMemTotal le soglie includono un margine per queste riserve. Ad esempio, una configurazione destinata ai dispositivi da 16 GB specifica un valore minimumRequiredMemTotal di 13312 (13 GiB) anziché 16384 (16 GiB) e un livello per i dispositivi da 12 GB specifica 10240 (10 GiB). In questo modo, ogni dispositivo corrisponde al livello previsto, indipendentemente dalle variazioni delle riserve a livello di scheda.

memVisible

Il limite di memoria flessibile (memory.high) in MiB applicato ai processi visibili.

memNotVisible

Il limite di memoria flessibile (memory.high) in MiB applicato ai processi non visibili.

swapVisible

Il limite di spazio di swap (memory.swap.max) in MiB applicato ai processi visibili.

swapNotVisible

Il limite di spazio di swap (memory.swap.max) in MiB applicato ai processi non visibili.

Limiti della piattaforma standard

In Android 17 e versioni successive, la piattaforma specifica i limiti standard di memoria e swap in /system/etc/memory-limiter-config.xml tra i livelli di RAM fisica comuni:

RAM fisica di destinazione MemTotal
minima richiesta
(minimumRequiredMemTotal)
Limite di memoria visibile
(memVisible)
Limite di memoria non visibile
(memNotVisible)
Limite di swap visibile
(swapVisible)
Limite di swap non visibile
(swapNotVisible)
16 GB 13.312 MiB (13 GiB) 10.240 MiB (10 GiB) 5120 MiB (5 GiB) 5120 MiB (5 GiB) 5120 MiB (5 GiB)
12 GB 10.240 MiB (10 GiB) 8192 MiB (8 GiB) 4096 MiB (4 GiB) 4096 MiB (4 GiB) 4096 MiB (4 GiB)
8 GB 6656 MiB (6,5 GiB) 5120 MiB (5 GiB) 3072 MiB (3 GiB) 3072 MiB (3 GiB) 3072 MiB (3 GiB)
6 GB 4608 MiB (4,5 GiB) 4096 MiB (4 GiB) 2048 MiB (2 GiB) 2048 MiB (2 GiB) 2048 MiB (2 GiB)
4 GB 3072 MiB (3 GiB) 2048 MiB (2 GiB) 1024 MiB (1 GiB) 1024 MiB (1 GiB) 1024 MiB (1 GiB)

Principi dei limiti di memoria

La configurazione di Memory Limiter è progettata in base ai seguenti principi della piattaforma:

  • Coerenza dell'ecosistema e compatibilità delle app: limiti di memoria coerenti su tutti i dispositivi garantiscono prestazioni delle app prevedibili in tutto l'ecosistema Android. Le app vengono sviluppate e testate in base alle aspettative di memoria standard; il mantenimento di limiti di piattaforma uniformi evita limitazioni impreviste o terminazioni premature.

  • Allocazione proporzionale delle risorse: i limiti della piattaforma vengono calibrati in base alla capacità della RAM fisica:

    • Processi visibili: circa 1/2-2/3 della RAM fisica totale allocata per supportare i carichi di lavoro di UI, composizione e rendering attivi.
    • Processi non visibili: circa 1/4-1/3 della RAM fisica totale allocata per le attività in background.
  • Applicabilità universale: i limiti si applicano in modo uniforme a tutti i processi delle applicazioni sul dispositivo (UID >= 10000), incluse le app di sistema e OEM preinstallate. Memory Limiter non supporta le liste consentite per esentare app specifiche, garantendo una gestione equa della memoria in tutte le app.

  • Assenza di API di query di runtime: in Android 17 e versioni successive, le app non possono eseguire query a livello di programmazione sui limiti di memoria assegnati in fase di runtime. I limiti della piattaforma standard sono definiti con margini generosi in modo che le app ben funzionanti operino senza vincoli durante l'utilizzo normale.

  • Recupero del kernel e dinamiche di swap: quando un processo di app si avvicina al limite memory.high, il kernel Linux indirizza il recupero della memoria in modo specifico al cgroup del processo. Ciò include l'eliminazione delle pagine di backup dei file inattive e lo scambio delle pagine anonime fredde in ZRAM, mantenendo la fluidità del sistema senza imporre una pressione globale sulla memoria.

Comandi shell

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

am memory-limiter <SUB-COMMAND>

stato

Il sottocomando status segnala lo stato operativo e le metriche attive 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 Memory Limiter sta monitorando attivamente i processi.
visibleMem e notVisibleMem
I limiti di memoria assoluti calcolati (memory.high) attualmente applicati per ogni stato di visibilità.
visibleSwap e notVisibleSwap
I limiti di swap assoluti calcolati (memory.swap.max) attualmente applicati per ogni stato di visibilità.
events
Il numero di volte in cui un processo ha superato il limite assegnato.
processes
Il numero attuale di processi monitorati.

ignora

Il sottocomando ignore esclude temporaneamente un UID specifico o tutti i processi dalla limitazione della memoria. Questa opzione è utile per il benchmarking delle prestazioni, i test di stress o la diagnosi del comportamento della memoria:

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

manuale

Il sottocomando manual sostituisce i limiti calcolati per un processo specifico in base all'ID processo (PID) con un valore assoluto personalizzato in byte. Il valore deve essere un numero intero, ma può includere il suffisso MB per indicare MiB o il suffisso GB per indicare GiB:

# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1073741824
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1024MB
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1GB
# Remove the manual override for PID 1234
adb shell am memory-limiter manual 1234 none

Le sostituzioni manuali si applicano solo per la durata dell'istanza del processo specifica. Se il processo viene riavviato, torna ai limiti standard in base al suo stato.

Una sostituzione manuale non può superare i limiti fisici del sistema.