Arbeitsspeicherbegrenzer

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:

ProzessstatusArbeitsspeicherlimit
PERSISTENTUneingeschränkt
PERSISTENT_UIUneingeschränkt
TOPSichtbar
BOUND_TOPSichtbar
FOREGROUND_SERVICENicht sichtbar
BOUND_FOREGROUND_SERVICENicht sichtbar
IMPORTANT_FOREGROUNDSichtbar
IMPORTANT_BACKGROUNDNicht sichtbar
TRANSIENT_BACKGROUNDNicht sichtbar
BACKUPNicht sichtbar
SERVICENicht sichtbar
RECEIVERNicht sichtbar
TOP_SLEEPINGSichtbar
HEAVY_WEIGHTNicht sichtbar
HOMENicht sichtbar
LAST_ACTIVITYNicht sichtbar
CACHED_ACTIVITYIm Cache
CACHED_ACTIVITY_CLIENTIm Cache
CACHED_RECENTIm Cache
CACHED_EMPTYIm 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 1 sein.
minimumRequiredMemTotal

Der 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/meminfo gemeldeten 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 MemTotal niedriger ist als der beworbene physische DRAM des Geräts, minimumRequiredMemTotal Schwellenwerte enthalten einen Spielraum für diese Ausnahmen. Beispielsweise wird in einer Konfiguration für Geräte mit 16 GB ein minimumRequiredMemTotal-Wert von 13312 (13 GiB) anstelle von 16384 (16 GiB) angegeben und für Geräte mit 12 GB wird 10240 (10 GiB) angegeben. So wird sichergestellt, dass jedes Gerät unabhängig von den Ausnahmen auf Boardebene der vorgesehenen Stufe entspricht.

memVisible

Das weiche Arbeitsspeicherlimit (memory.high) in MiB, das auf sichtbare Prozesse angewendet wird.

memNotVisible

Das weiche Arbeitsspeicherlimit (memory.high) in MiB, das auf nicht sichtbare Prozesse angewendet wird.

swapVisible

Das Auslagerungsspeicherlimit (memory.swap.max) in MiB, das auf sichtbare Prozesse angewendet wird.

swapNotVisible

Das 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.high nä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 status

Beispielausgabe:

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.
visibleMem und notVisibleMem
Die berechneten absoluten Arbeitsspeicherlimits (memory.high), die derzeit für jeden Sichtbarkeitsstatus erzwungen werden.
visibleSwap und notVisibleSwap
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.