Android 17 und höher umfasst den Memory Limiter, einen Systemdienst, der die Arbeitsspeichernutzung von Anwendungsprozessen mithilfe von Linux cgroup v2 überwacht und begrenzt. Der Memory Limiter verhindert, dass einzelne Apps unverhältnismäßig viel Systemspeicher verbrauchen. Dadurch wird der Gesamtspeicherdruck reduziert und aggressive Low-Memory-Kills von kritischen Vordergrund- und im Cache gespeicherten Prozessen verhindert.
Mechanismus
Der Memory Limiter ist in den Activity Manager Service (AMS) integriert, um Ereignisse im Lebenszyklus von Prozessen und Statusänderungen zu verfolgen. Der Memory Limiter erzwingt Speicherlimits mithilfe der Linux-Kernel-cgroup-v2-Hierarchie.
Damit der Memory Limiter funktioniert, müssen im Kernel des Geräts cgroup v2 und der memory-Controller aktiviert sein. Der Dienst basiert speziell auf den folgenden Attributen:
memory.high- Ein weiches Limit. Wenn es überschritten wird, wird der Prozess gedrosselt und der Kernel versucht, Seiten proaktiv aus diesem Prozess freizugeben.
memory.swap.max- Begrenzt die maximale Menge an Auslagerungsspeicher (z. B. ZRAM), die der Prozess verwenden kann.
Auswirkungen auf Apps
Apps, die innerhalb ihrer Speicherlimits arbeiten, sind vom Memory Limiter nicht betroffen.
Wenn eine App das Limit memory.high überschreitet, gibt der Kernel den sauberen, dateibasierten Speicher der App frei und lagert den inaktiven anonymen Speicher aus, damit die App innerhalb des Limits bleibt. Aufgrund dieser Seitenfreigabe und Auslagerungsaktivität kann es zu einer vorübergehenden Drosselung der Ausführung der App kommen, sie wird aber weiterhin ausgeführt.
In Extremsituationen, z. B. bei einem unkontrollierten Speicherleck, bei dem die App weiterhin anonymen Speicher zuweist, nachdem der Auslagerungsspeicher erschöpft ist, kann die App keinen Speicher zuweisen und wird beendet.
Prozessmonitoring
Der Memory Limiter überwacht standardmäßig App-Prozesse (UID >= 10000). Kernsystemprozesse sind ausgenommen, um die grundlegende Systemstabilität zu gewährleisten.
Der Memory Limiter weist Speicherlimits basierend auf dem Sichtbarkeitsstatus des Prozesses zu:
Sichtbare Prozesse:Prozesse, die eine für Nutzer sichtbare UI hosten (z. B. die derzeit fokussierte Aktivität oder eine aktive Interaktion). Da für die Anzeige einer UI ein größerer Arbeitssatz für Rendering- und Grafikpipelines erforderlich ist, erhalten sichtbare Prozesse ein großzügigeres Speicherlimit.
Nicht sichtbare Prozesse:Prozesse, die im Hintergrund arbeiten, ohne eine UI zu präsentieren (z. B. Hintergrunddienste oder Broadcast-Empfänger). Da sie keine UI-Komponenten rendern, unterliegen sie einem restriktiveren Limit.
In der folgenden Tabelle werden bestimmte Prozessstatus den Klassifizierungen für Speicherlimits zugeordnet:
| Prozessstatus | Arbeitsspeicherlimit |
|---|---|
PERSISTENT | Uneingeschränkt |
PERSISTENT_UI | Uneingeschränkt |
TOP | Sichtbar |
BOUND_TOP | Sichtbar |
FOREGROUND_SERVICE | Nicht sichtbar |
BOUND_FOREGROUND_SERVICE | Nicht sichtbar |
IMPORTANT_FOREGROUND | Sichtbar |
IMPORTANT_BACKGROUND | Nicht sichtbar |
TRANSIENT_BACKGROUND | Nicht sichtbar |
BACKUP | Nicht sichtbar |
SERVICE | Nicht sichtbar |
RECEIVER | Nicht sichtbar |
TOP_SLEEPING | Sichtbar |
HEAVY_WEIGHT | Nicht sichtbar |
HOME | Nicht sichtbar |
LAST_ACTIVITY | Nicht sichtbar |
CACHED_ACTIVITY | Im Cache |
CACHED_ACTIVITY_CLIENT | Im Cache |
CACHED_RECENT | Im Cache |
CACHED_EMPTY | Im Cache |
Im Cache-Status werden Prozesse eingefroren und dann maximal freigegeben.
Wenn ein Prozess das zugewiesene Limit memory.high überschreitet, erkennt der Memory Limiter das Ereignis und kann Diagnoseaktionen auslösen, z. B. ein Speicherprofil erfassen oder eine Anomalie in statsd protokollieren.
Konfiguration
In Android 17 und höher wird der Memory Limiter mit der Standardkonfigurationsdatei der Plattform auf der Systempartition unter /system/etc/memory-limiter-config.xml konfiguriert.
XML-Format
Die Konfigurationsdatei folgt dem in memory-limiter-config.xsd definierten Schema. Die Datei definiert mehrere Limitsätze, die nach verfügbarem Speicher sortiert sind. Der Dienst wählt den höchsten übereinstimmenden Limitsatz basierend auf dem verfügbaren RAM des Geräts aus. Alle Speicherwerte werden in Mebibyte (MiB) angegeben.
<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- Eine positive Ganzzahl, die die Konfigurationsversion identifiziert. Sie muss
1sein. minimumRequiredMemTotalDas minimale System
MemTotal(in MiB, einschließlich Untergrenze), das für die Anwendung dieses Limitsatzes erforderlich ist. Der Dienst vergleicht diesen Wert mit dem vom Kernel in/proc/meminfogemeldeten gesamten verfügbaren Speicher.Informationen zu Speicherausnahmen und -grenzen:Der Kernelwert
MemTotalgibt den physischen DRAM an, auf den das Betriebssystem zugreifen kann, nachdem die Speicherausnahmen für Hardware und Firmware abgezogen wurden. Durch Ausnahmen wird RAM für spezielle Hardwarekomponenten wie die GPU, das Baseband-Modem, den Kamera-ISP, sichere Ausführungsumgebungen und Hypervisoren reserviert. In der Regel werden zwischen 500 MiB und über 2 GiB physischer Speicher verbraucht.Da
MemTotalniedriger ist als der angegebene physische DRAM des Geräts,minimumRequiredMemTotalSchwellenwerte enthalten einen Spielraum für diese Ausnahmen. Beispielsweise wird in einer Konfiguration für Geräte mit 16 GB eine Untergrenze von14336(14 GiB) anstelle von16384(16 GiB) angegeben und für Geräte mit 12 GB eine Untergrenze von9216(9 GiB). So wird sichergestellt, dass jedes Gerät unabhängig von den Ausnahmen auf Boardebene der vorgesehenen Stufe entspricht.maximumAllowedMemTotalDas maximale System
MemTotal(in MiB, ohne Obergrenze), das für die Anwendung dieses Limitsatzes erforderlich ist. WennMemTotalgrößer oder gleich dieser Obergrenze ist, wird der Limitsatz nicht angewendet. Bei Geräten mit 18.432 MiB oder mehr oder bei Geräten mit weniger als 3.200 MiB wendet der Memory Limiter keine Limits an, sodass Prozesse nicht eingeschränkt werden.memVisibleDas weiche Speicherlimit (
memory.high) in MiB, das auf sichtbare Prozesse angewendet wird.memNotVisibleDas weiche Speicherlimit (
memory.high) in MiB, das auf nicht sichtbare Prozesse angewendet wird.swapVisibleDas Auslagerungsspeicherlimit (
memory.swap.max) in MiB, das auf sichtbare Prozesse angewendet wird.swapNotVisibleDas Auslagerungsspeicherlimit (
memory.swap.max) in MiB, das auf nicht sichtbare Prozesse angewendet wird.
Standardlimits der Plattform
In Android 17 und höher gibt die Plattform Standardlimits für Speicher und Auslagerungsspeicher
in /system/etc/memory-limiter-config.xml für gängige physische RAM
Stufen mit [floor, ceiling) Bereichsintervallen an:
| Ziel-RAM |
MemTotal-Bereich [Untergrenze, Obergrenze) (minimumRequiredMemTotal, maximumAllowedMemTotal) |
Speicherlimit für sichtbare Prozesse (memVisible) |
Speicherlimit für nicht sichtbare Prozesse (memNotVisible) |
Auslagerungsspeicherlimit für sichtbare Prozesse (swapVisible) |
Auslagerungsspeicherlimit für nicht sichtbare Prozesse (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) |
Prinzipien für Speicherlimits
Die Memory Limiter-Konfiguration basiert auf den folgenden Plattformprinzipien:
Konsistenz des Ökosystems und App-Kompatibilität:Einheitliche Speicherlimits auf allen Geräten sorgen für eine vorhersehbare App-Leistung im gesamten Android-Ökosystem. Apps werden anhand von Standardspeichererwartungen entwickelt und getestet. Durch einheitliche Plattformlimits werden unerwartete Drosselungen oder vorzeitige Beendigungen vermieden.
Proportionale Ressourcenzuweisung:Die Plattformlimits werden entsprechend der physischen RAM-Kapazität kalibriert:
- Sichtbare Prozesse:Etwa 1/2 bis 2/3 des gesamten physischen RAM werden zugewiesen, um aktive UI-, Kompositions- und Rendering-Arbeitslasten zu unterstützen.
- Nicht sichtbare Prozesse:Etwa 1/4 bis 1/3 des gesamten physischen RAM werden für Hintergrundaufgaben zugewiesen.
Universelle Anwendbarkeit:Die Limits gelten einheitlich für alle Anwendungsprozesse auf dem Gerät (UID >= 10000), einschließlich vorinstallierter System- und OEM-Apps. Der Memory Limiter unterstützt keine Zulassungslisten, um bestimmte Apps auszunehmen. So wird eine faire Speicherverwaltung für alle Apps gewährleistet.
Keine APIs für Laufzeitabfragen:In Android 17 und höher können Apps ihre zugewiesenen Speicherlimits nicht programmatisch zur Laufzeit abfragen. Die Standardlimits der Plattform sind mit großzügigen Spielräumen definiert, sodass gut funktionierende Apps bei normaler Nutzung ohne Einschränkungen ausgeführt werden können.
Kernel-Freigabe und Auslagerungsdynamik:Wenn ein App-Prozess das Limit
memory.higherreicht, richtet der Linux-Kernel die Speicherfreigabe speziell auf diese Prozess-Cgroup aus. Dazu gehört das Löschen inaktiver dateibasierter Seiten und das Auslagern kalter anonymer Seiten in ZRAM, wodurch die Systemflüssigkeit erhalten bleibt, ohne den globalen Speicherdruck zu erhöhen.
Shell-Befehle
Mit dem Befehl am memory-limiter können Entwickler und Systemintegratoren zur Laufzeit mit dem Dienst interagieren, um ihn zu entwickeln, zu testen und Fehler zu beheben:
am memory-limiter <SUB-COMMAND>Status
Der Unterbefehl status meldet den Betriebsstatus und die aktiven Messwerte des Memory Limiter:
adb shell am memory-limiter statusHier ist ein Beispiel für die Ausgabe:
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
Wichtige Felder in der Ausgabe sind:
monitoring- Gibt an, ob der Memory Limiter Prozesse aktiv überwacht.
visibleMemundnotVisibleMem- Die berechneten absoluten Speicherlimits (
memory.high), die derzeit für jeden Sichtbarkeitsstatus erzwungen werden. visibleSwapundnotVisibleSwap- Die berechneten absoluten Auslagerungsspeicherlimits (
memory.swap.max), die derzeit für jeden Sichtbarkeitsstatus erzwungen werden. events- Die Anzahl der Male, die ein Prozess sein zugewiesenes Limit überschritten hat.
processes- Die aktuelle Anzahl der überwachten Prozesse.
Ignorieren
Mit dem Unterbefehl ignore wird eine bestimmte UID oder alle Prozesse vorübergehend von der Speicherbegrenzung ausgeschlossen. Dies ist nützlich für Leistungsbenchmarks, Stresstests oder die Diagnose des Speicherverhaltens:
# 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
Manuell
Mit dem Unterbefehl manual werden die berechneten Limits für einen bestimmten Prozess anhand der Prozess-ID (PID) mit einem benutzerdefinierten absoluten Wert in Byte überschrieben. Der Wert muss eine Ganzzahl sein, kann aber das Suffix MB für MiB oder das Suffix GB für GiB enthalten:
# 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
Manuelle Überschreibungen gelten nur für die Lebensdauer dieser bestimmten Prozessinstanz. Wenn der Prozess neu gestartet wird, werden die Standardlimits basierend auf dem Status wiederhergestellt.
Eine manuelle Überschreibung darf die physischen Systemlimits nicht überschreiten.