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'interruzione 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 basata su 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 elaborazione | Limite di memoria |
|---|---|
PERSISTENT | Senza restrizioni |
PERSISTENT_UI | Senza restrizioni |
TOP | Visibile |
BOUND_TOP | Visibile |
FOREGROUND_SERVICE | Non visibile |
BOUND_FOREGROUND_SERVICE | Non visibile |
IMPORTANT_FOREGROUND | Visibile |
IMPORTANT_BACKGROUND | Non visibile |
TRANSIENT_BACKGROUND | Non visibile |
BACKUP | Non visibile |
SERVICE | Non visibile |
RECEIVER | Non visibile |
TOP_SLEEPING | Visibile |
HEAVY_WEIGHT | Non visibile |
HOME | Non visibile |
LAST_ACTIVITY | Non visibile |
CACHED_ACTIVITY | Memorizzata nella cache |
CACHED_ACTIVITY_CLIENT | Memorizzata nella cache |
CACHED_RECENT | Memorizzata nella cache |
CACHED_EMPTY | Memorizzata 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 acquisire un profilo di memoria o registrare un'anomalia in statsd.
Configurazione
In Android 17 e versioni successive, Memory Limiter viene configurato utilizzando il file di configurazione della piattaforma standard nella partizione di sistema in /system/etc/memory-limiter-config.xml.
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 di configurazione. Deve essere
1. minimumRequiredMemTotalIl valore
MemTotalminimo del sistema (in MiB, piano inclusivo) 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 sui carve-out e sui limiti di memoria: il valore
MemTotaldel kernel riflette la DRAM fisica accessibile al sistema operativo dopo la detrazione dei carve-out di memoria hardware e firmware. I carve-out riservano la RAM per componenti hardware specializzati come GPU, modem baseband, ISP della fotocamera, ambienti di esecuzione sicuri e hypervisor, in genere consumando tra 500 MiB e oltre 2 GiB di memoria fisica.Poiché
MemTotalè inferiore alla DRAM fisica pubblicizzata del dispositivo,minimumRequiredMemTotalle soglie includono un margine per questi carve-out. Ad esempio, una configurazione destinata ai dispositivi da 16 GB specifica un piano di14336(14 GiB) anziché16384(16 GiB) e un livello per i dispositivi da 12 GB specifica un piano di9216(9 GiB). In questo modo, ogni dispositivo corrisponde al livello previsto, indipendentemente dalle variazioni di carve-out a livello di scheda.maximumAllowedMemTotalIl valore
MemTotalmassimo del sistema (in MiB, limite superiore esclusivo) per l'applicazione di questo set di limiti. SeMemTotalè maggiore o uguale a questo limite superiore, il set di limiti non viene applicato. Per i dispositivi con 18.432 MiB o più o per i dispositivi con meno di 3.200 MiB, Memory Limiter non applica limiti, lasciando i processi senza vincoli.memVisibleIl limite di memoria flessibile (
memory.high) in MiB applicato ai processi visibili.memNotVisibleIl limite di memoria flessibile (
memory.high) in MiB applicato ai processi non visibili.swapVisibleIl limite di spazio di swap (
memory.swap.max) in MiB applicato ai processi visibili.swapNotVisibleIl 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 utilizzando intervalli di intervallo [floor, ceiling):
| RAM fisica di destinazione |
Intervallo MemTotal [floor, ceiling) (minimumRequiredMemTotal, maximumAllowedMemTotal) |
Limite di memoria visibile (memVisible) |
Limite di memoria non visibile (memNotVisible) |
Limite di swap visibile (swapVisible) |
Limite di swap non visibile (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | [14336, 18432) MiB |
10.240 MiB (10 GiB) | 5.120 MiB (5 GiB) | 5.120 MiB (5 GiB) | 5.120 MiB (5 GiB) |
| 12 GB | [9216, 14336) MiB |
8.192 MiB (8 GiB) | 4.096 MiB (4 GiB) | 4.096 MiB (4 GiB) | 4.096 MiB (4 GiB) |
| 8 GB | [6800, 9216) MiB |
5.120 MiB (5 GiB) | 3.072 MiB (3 GiB) | 3.072 MiB (3 GiB) | 3.072 MiB (3 GiB) |
| 6 GB | [4800, 6800) MiB |
4.096 MiB (4 GiB) | 2.048 MiB (2 GiB) | 2.048 MiB (2 GiB) | 2.048 MiB (2 GiB) |
| 4 GB | [3200, 4800) MiB |
2.048 MiB (2 GiB) | 1.024 MiB (1 GiB) | 1.024 MiB (1 GiB) | 1.024 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 basate su file inattive e lo scambio delle pagine anonime fredde con ZRAM, mantenendo la fluidità del sistema senza imporre una pressione sulla memoria globale.
Comandi della 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 statusDi seguito è riportato un 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.
visibleMemenotVisibleMem- I limiti di memoria assoluti calcolati (
memory.high) attualmente applicati per ogni stato di visibilità. visibleSwapenotVisibleSwap- 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.