Limitatore di memoria

Android 17 e versioni successive includono il limitatore di memoria, un servizio di sistema che monitora e limita la memoria utilizzata dei processi delle applicazioni utilizzando cgroup v2 di Linux. Il limitatore di memoria impedisce alle singole app di consumare una quantità sproporzionata di memoria di sistema, riducendo la pressione complessiva sulla memoria e impedendo l'interruzione aggressiva per memoria insufficiente dei processi in primo piano e memorizzati nella cache critici.

Meccanismo

Il limitatore di memoria si integra con l'Activity Manager Service (AMS) per monitorare gli eventi del ciclo di vita dei processi e le modifiche di stato. Il limitatore di memoria applica i limiti di memoria utilizzando la gerarchia cgroup v2 del kernel Linux.

Per supportare il limitatore di memoria, il kernel del dispositivo deve abilitare cgroup v2 e il controller memory. Il servizio si basa in particolare sui seguenti attributi:

memory.high
Un limite flessibile. Se questo limite viene superato, il processo viene limitato e il kernel tenta il recupero proattivo delle 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 dal limitatore di memoria.

Quando un'app supera il limite di memory.high, il kernel rimuove la memoria pulita dell'app supportata da file e scambia la memoria anonima non attiva per mantenere l'app entro il limite. A seguito di questa attività di espulsione e scambio di 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 l'esaurimento dello spazio di swap, l'app non riesce ad allocare memoria e viene terminata.

Monitoraggio dei processi

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

Il limitatore di memoria 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 attiva o un'interazione attiva). Poiché la visualizzazione di una UI richiede un set di lavoro più ampio per il rendering e le pipeline grafiche, i processi visibili ricevono un limite di memoria più generoso.

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

La tabella seguente mappa 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 di memory.high assegnato, il limitatore di memoria rileva l'evento e può attivare azioni di diagnostica, come l'acquisizione di un profilo di memoria o la registrazione di un'anomalia in statsd.

Configurazione

In Android 17 e versioni successive, il limitatore di memoria viene configurato utilizzando il file di configurazione della piattaforma standard nella partizione di sistema 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 boundaries account for MemTotal excluding carve-outs. -->
    <limitSet>
      <!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
      <memVisible>10240</memVisible>
      <memNotVisible>5120</memNotVisible>
      <swapVisible>5120</swapVisible>
      <swapNotVisible>5120</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
      <minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
      <memVisible>5120</memVisible>
      <memNotVisible>3072</memNotVisible>
      <swapVisible>3072</swapVisible>
      <swapNotVisible>3072</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
      <minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
      <memVisible>4096</memVisible>
      <memNotVisible>2048</memNotVisible>
      <swapVisible>2048</swapVisible>
      <swapNotVisible>2048</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
      <minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
      <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

La quantità minima di MemTotal di sistema (in MiB, valore minimo incluso) richiesta per l'applicazione di questo set di limiti. Il servizio valuta questo valore rispetto alla memoria totale disponibile indicata dal kernel in /proc/meminfo.

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

Poiché MemTotal è inferiore alla DRAM fisica pubblicizzata del dispositivo, le soglie di minimumRequiredMemTotal includono un margine per queste esclusioni. Ad esempio, una configurazione che ha come target dispositivi da 16 GB specifica un valore minimo di 14336 (14 GiB) anziché 16384 (16 GiB), mentre un livello per dispositivi da 12 GB specifica un valore minimo di 9216 (9 GiB). In questo modo, ogni dispositivo corrisponde al livello previsto, indipendentemente dalle variazioni di carve-out a livello di scheda.

maximumAllowedMemTotal

Il valore massimo di MemTotal di sistema (in MiB, limite esclusivo) per l'applicazione di questo set di limiti. Se MemTotal è maggiore o uguale a questo limite massimo, il limite impostato non viene applicato. Per i dispositivi con almeno 18.432 MiB o con meno di 3200 MiB, il limitatore di memoria non applica limiti, lasciando i processi senza vincoli.

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 dello 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 di memoria e swap standard in /system/etc/memory-limiter-config.xml per i livelli di RAM fisica comuni utilizzando gli intervalli di intervallo [floor, ceiling):

RAM fisica target Intervallo MemTotal [minimo, massimo)
(minimumRequiredMemTotal,
maximumAllowedMemTotal)
Limite di memoria visibile
(memVisible)
Limite di memoria non visibile
(memNotVisible)
Limite di scambio visibile
(swapVisible)
Limite di scambio non visibile
(swapNotVisible)
16 GB [14336, 18432) MiB 10.240 MiB (10 GiB) 5120 MiB (5 GiB) 5120 MiB (5 GiB) 5120 MiB (5 GiB)
12 GB [9216, 14336) MiB 8192 MiB (8 GiB) 4096 MiB (4 GiB) 4096 MiB (4 GiB) 4096 MiB (4 GiB)
8 GB [6800, 9216) MiB 5120 MiB (5 GiB) 3072 MiB (3 GiB) 3072 MiB (3 GiB) 3072 MiB (3 GiB)
6 GB [4800, 6800) MiB 4096 MiB (4 GiB) 2048 MiB (2 GiB) 2048 MiB (2 GiB) 2048 MiB (2 GiB)
4 GB [3200, 4800) MiB 2048 MiB (2 GiB) 1024 MiB (1 GiB) 1024 MiB (1 GiB) 1024 MiB (1 GiB)

Principi dei limiti di memoria

La configurazione del limitatore di memoria è 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 uniformi della piattaforma evita limitazioni impreviste o terminazioni premature.

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

    • Processi visibili: viene allocata circa la metà o i due terzi della RAM fisica totale per supportare i workload attivi di UI, composizione e rendering.
    • Processi non visibili:viene allocato circa 1/4-1/3 della RAM fisica totale 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. Il limitatore di memoria non supporta le liste consentite per esentare app specifiche, garantendo una gestione equa della memoria in tutte le app. Per configurare i floor di protezione con ottimizzazione automatica di memory.min sui daemon interattivi critici e i ceiling più rigidi calibrati sul workload di memory.high su singole app e servizi di sistema, consulta Budget di memoria di sistema.

  • Assenza di API di query di runtime:in Android 17 e versioni successive, le app non possono eseguire query in modo programmatico sui limiti di memoria assegnati in fase di runtime. I limiti standard della piattaforma sono definiti con margini generosi, in modo che le app con un comportamento corretto funzionino senza vincoli durante il normale utilizzo.

  • Dinamiche di recupero e scambio del kernel:quando un processo dell'app si avvicina al limite di memory.high, il kernel Linux indirizza il recupero della memoria in modo specifico al cgroup di quel processo. Ciò include l'eliminazione delle pagine inattive supportate da file e lo scambio di pagine anonime non utilizzate con ZRAM, mantenendo la fluidità del sistema senza imporre una pressione della memoria globale.

Comandi shell

Il comando am memory-limiter consente agli sviluppatori e agli 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 di attività e le metriche attive del limitatore di memoria:

adb shell am memory-limiter status

Di seguito è riportato un esempio di output:

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 di memoria 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 scambio 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. Questo è utile per il benchmarking delle prestazioni, gli stress test 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 esegue l'override dei limiti calcolati per un processo specifico in base all'ID di 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

Gli override manuali si applicano solo alla durata di quella specifica istanza di processo. Se il processo viene riavviato, torna ai limiti standard in base al suo stato.

Un override manuale non può superare i limiti fisici dell'impianto.