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 Arbeitsspeicher belegen. Dadurch wird der Gesamtdruck auf den Arbeitsspeicher 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 Zustandsänderungen zu verfolgen. Der Memory Limiter erzwingt Arbeitsspeicherlimits 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 zurückzufordern.
memory.swap.max- Begrenzt die maximale Menge an Auslagerungsspeicher (z. B. ZRAM), die der Prozess verwenden kann.
Auswirkungen auf Apps
Apps, die innerhalb ihrer Arbeitsspeicherlimits arbeiten, sind vom Memory Limiter nicht betroffen.
Wenn eine App das Limit memory.high überschreitet, entfernt der Kernel den sauberen, dateibasierten Arbeitsspeicher der App und lagert den inaktiven anonymen Arbeitsspeicher aus, damit die App innerhalb des Limits bleibt. Aufgrund dieser Seitenentfernung und Auslagerungsaktivität kann es zu einer vorübergehenden Drosselung der Ausführung der App kommen, sie wird aber weiterhin ausgeführt.
In extremen Situationen, z. B. bei einem unkontrollierten Speicherleck, bei dem die App weiterhin anonymen Arbeitsspeicher zuweist, nachdem der Auslagerungsspeicher erschöpft ist, kann die App keinen Arbeitsspeicher 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 Arbeitsspeicherlimits basierend auf dem Sichtbarkeitsstatus des Prozesses zu:
Sichtbare Prozesse:Prozesse, die eine für Nutzer sichtbare Benutzeroberfläche hosten (z. B. die derzeit fokussierte Aktivität oder eine aktive Interaktion). Da für die Anzeige einer Benutzeroberfläche ein größerer Arbeitssatz für Rendering- und Grafikpipelines erforderlich ist, erhalten sichtbare Prozesse ein großzügigeres Arbeitsspeicherlimit.
Nicht sichtbare Prozesse:Prozesse, die im Hintergrund arbeiten, ohne eine Benutzeroberfläche 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 Prozesszustände den Klassifizierungen für Arbeitsspeicherlimits 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-Zustand werden Prozesse eingefroren und dann maximal zurückgefordert.
Wenn ein Prozess das zugewiesene Limit memory.high überschreitet, erkennt der Memory Limiter das Ereignis und kann Diagnoseaktionen auslösen, z. B. ein Arbeitsspeicherprofil erfassen oder eine Anomalie in statsd protokollieren.
Konfiguration
In Android 17 und höher bietet der Memory Limiter eine Standardkonfigurationsdatei für die Plattform auf der Systempartition und unterstützt optionale gerätespezifische Überschreibungen auf der vendor-Partition:
- Standardkonfiguration des Systems:
/system/etc/memory-limiter-config.xml - Überschreibung des Anbieters (optional) :
/vendor/etc/memory-limiter-config.xml
Der Memory Limiter sucht zuerst nach /vendor/etc/memory-limiter-config.xml.
Wenn die Datei vorhanden ist, wird die Anbieterkonfiguration verwendet. Andernfalls wird auf die Standardkonfiguration des Systems in /system/etc/memory-limiter-config.xml zurückgegriffen.
XML-Format
Die Konfigurationsdatei folgt dem in memory-limiter-config.xsd definierten Schema. Die Datei definiert mehrere Limitsätze, die nach verfügbarem Arbeitsspeicher sortiert sind. Der Dienst wählt den höchsten passenden Limitsatz basierend auf dem verfügbaren RAM des Geräts aus. Alle Arbeitsspeicherwerte werden in Mebibyte (MiB) angegeben.
<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- Eine positive Ganzzahl, die die Konfigurationsversion identifiziert. Dieser Wert muss
1sein. minimumRequiredMemTotalDer minimale
MemTotal-Wert des Systems (in MiB), der erforderlich ist, damit dieser Limitsatz angewendet wird. Der Dienst vergleicht diesen Wert mit dem vom Kernel in/proc/meminfogemeldeten verfügbaren Gesamtarbeitsspeicher.Informationen zu Arbeitsspeicherausnahmen:Der
MemTotal-Wert des Kernels gibt den physischen DRAM an, auf den das Betriebssystem zugreifen kann, nachdem die Arbeitsspeicherausnahmen 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 dabei zwischen 500 MiB und über 1 GiB physischer Arbeitsspeicher belegt.Da
MemTotalniedriger ist als der beworbene 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 einminimumRequiredMemTotal-Wert von13312(13 GiB) anstelle von16384(16 GiB) angegeben und für Geräte mit 12 GB wird10240(10 GiB) angegeben. So wird sichergestellt, dass jedes Gerät unabhängig von den Ausnahmen auf Boardebene der vorgesehenen Stufe entspricht.memVisibleDas weiche Arbeitsspeicherlimit (
memory.high) in MiB, das auf sichtbare Prozesse angewendet wird.memNotVisibleDas weiche Arbeitsspeicherlimit (
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 in /system/etc/memory-limiter-config.xml Standardlimits für Arbeitsspeicher und Auslagerungsspeicher für gängige physische RAM-Stufen an:
| Ziel-RAM |
Mindestens erforderlicher MemTotal (minimumRequiredMemTotal) |
Arbeitsspeicherlimit für sichtbare Prozesse (memVisible) |
Arbeitsspeicherlimit für nicht sichtbare Prozesse (memNotVisible) |
Auslagerungsspeicherlimit für sichtbare Prozesse (swapVisible) |
Auslagerungsspeicherlimit für nicht sichtbare Prozesse (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | 13.312 MiB (13 GiB) | 10.240 MiB (10 GiB) | 5.120 MiB (5 GiB) | 5.120 MiB (5 GiB) | 5.120 MiB (5 GiB) |
| 12 GB | 10.240 MiB (10 GiB) | 8.192 MiB (8 GiB) | 4.096 MiB (4 GiB) | 4.096 MiB (4 GiB) | 4.096 MiB (4 GiB) |
| 8 GB | 6.656 MiB (6,5 GiB) | 5.120 MiB (5 GiB) | 3.072 MiB (3 GiB) | 3.072 MiB (3 GiB) | 3.072 MiB (3 GiB) |
| 6 GB | 4.608 MiB (4,5 GiB) | 4.096 MiB (4 GiB) | 2.048 MiB (2 GiB) | 2.048 MiB (2 GiB) | 2.048 MiB (2 GiB) |
| 4 GB | 3.072 MiB (3 GiB) | 2.048 MiB (2 GiB) | 1.024 MiB (1 GiB) | 1.024 MiB (1 GiB) | 1.024 MiB (1 GiB) |
Prinzipien für Arbeitsspeicherlimits
Die Konfiguration des Memory Limiter basiert auf den folgenden Plattformprinzipien:
Konsistenz des Ökosystems und App-Kompatibilität:Einheitliche Arbeitsspeicherlimits auf allen Geräten sorgen für eine vorhersehbare App-Leistung im gesamten Android-Ökosystem. Apps werden anhand von Standarderwartungen für den Arbeitsspeicher 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 Arbeitsspeicherverwaltung für alle Apps gewährleistet.
Keine APIs für Laufzeitabfragen:In Android 17 und höher können Apps ihre zugewiesenen Arbeitsspeicherlimits 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-Rückforderung und Auslagerungsdynamik:Wenn sich ein App-Prozess dem Limit
memory.highnähert, richtet der Linux-Kernel die Arbeitsspeicherrückforderung speziell auf diese Prozess-Cgroup aus. Dazu gehört das Entfernen inaktiver dateibasierter Seiten und das Auslagern kalter anonymer Seiten in ZRAM, wodurch die Systemflüssigkeit erhalten bleibt, ohne den globalen Arbeitsspeicherdruck 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 gibt den Betriebsstatus und die aktiven Messwerte des Memory Limiter aus:
adb shell am memory-limiter statusBeispielausgabe:
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 Arbeitsspeicherlimits (
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 Arbeitsspeicherbegrenzung ausgeschlossen. Dies ist nützlich für Leistungsbenchmarks, Stresstests oder die Diagnose des Arbeitsspeicherverhaltens:
# 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.