Arbeitsspeicherbegrenzer

Android 17 und höher unterstützt 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 zu viel Systemspeicher verbrauchen. Dadurch wird der systemspezifische Speicherbedarf reduziert und das aggressive Beenden kritischer Prozesse aufgrund von Speichermangel (Out-of-Memory, OOM) 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 des Linux-Kernel-Dateisystems cgroup v2.

Damit der Memory Limiter verwendet werden kann, muss der Gerätekernel cgroup v2 und den memory-Controller unterstützen. 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, Speicher freizugeben.
memory.swap.max
Begrenzt die Menge an Auslagerungsspeicher, die der Prozess verwenden kann.

Auswirkungen auf Apps

Apps, die ihre Speicherlimits nicht überschreiten, sind vom Memory Limiter nicht betroffen.

Wenn eine App das Limit memory.high überschreitet, entfernt der Kernel den dateibasierten Speicher der App und lagert den anonymen Speicher aus, damit die App das Limit nicht überschreitet. Aufgrund des Entfernens und Auslagerns wird die App möglicherweise langsamer ausgeführt.

Im Extremfall kann es passieren, dass die App weiterhin anonymen Speicher zuweist und der Auslagerungsspeicher des Geräts erschöpft ist. In diesem Fall kann die App keinen Speicher mehr zuweisen und stürzt wahrscheinlich ab.

Prozessmonitoring

Der Memory Limiter überwacht standardmäßig App-Prozesse (UID >= 10000). Systemprozesse sind in der Regel ausgenommen, um die Stabilität des Kernsystems zu überprüfen.

Der Memory Limiter weist Speicherlimits basierend auf dem Status des Prozesses zu:

  • Sichtbare Prozesse sind für den Nutzer wahrnehmbar, z. B. Aktivitäten im Vordergrund, Dienste im Vordergrund oder andere Zustände, die zu Rucklern führen können.

  • Nicht sichtbare Prozesse sind Hintergrundprozesse, die nicht mit dem Nutzer interagieren oder für ihn sichtbar sind.

In der folgenden Tabelle werden bestimmte Prozesszustände Speicherlimits 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 gespeichert
CACHED_ACTIVITY_CLIENTIm Cache gespeichert
CACHED_RECENTIm Cache gespeichert
CACHED_EMPTYIm Cache gespeichert

Im Cache gespeicherte Prozesse werden eingefroren und dann maximal freigegeben.

Wenn ein Prozess das zugewiesene Limit memory.high überschreitet, erkennt der Memory Limiter das Ereignis und kann Debugging-Aktionen auslösen, z. B. ein Speicherprofil erfassen oder eine Anomalie in statsd protokollieren.

Konfiguration

Konfigurieren Sie den Memory Limiter mit einer XML-Datei auf der Partition vendor. Mit der Konfiguration können Sie absolute Speicherlimits basierend auf den spezifischen Speicherbeschränkungen des Geräts anpassen.

  • Dateipfad:/vendor/etc/memory-limiter-config.xml

  • Standardkonfiguration:Wenn die Konfigurationsdatei nicht gefunden wird oder nicht lesbar oder ungültig ist, wird der Memory Limiter deaktiviert.

XML-Format

Die Konfigurationsdatei folgt dem in memory-limiter-config.xsd definierten Schema. In der Datei können Sie mehrere Limitsätze definieren. Der Dienst wählt basierend auf dem verfügbaren RAM des Geräts die beste Übereinstimmung aus. Alle Speicherwerte werden in Mebibyte (MiB) angegeben.

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <limitSet>
      <!-- Limits for a phone with at least 14G of ram: 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
Eine positive Ganzzahl, die die Konfigurationsversion angibt. Muss 1 sein.
minimumRequiredMemTotal
Der mindestens erforderliche verfügbare Systemspeicher, damit dieser Limitsatz gültig ist.
memVisible
Das Speicherlimit (memory.high), das für sichtbare Prozesse zulässig ist.
memNotVisible
Das Speicherlimit (memory.high), das für nicht sichtbare Prozesse zulässig ist.
swapVisible
Das Auslagerungslimit (memory.swap.max), das für sichtbare Prozesse zulässig ist.
swapNotVisible
Das Auslagerungslimit (memory.swap.max), das für nicht sichtbare Prozesse zulässig ist.

Konfiguration ändern

So ändern Sie systemweite Limits:

  1. Ändern Sie /vendor/etc/memory-limiter-config.xml.
  2. Starten Sie das Gerät neu oder starten Sie system_server neu, damit die Änderungen wirksam werden.

Shell-Befehle

Mit dem Befehl am memory-limiter können Sie und Entwickler zur Laufzeit mit dem Dienst interagieren, um ihn zu entwickeln und zu testen:

am memory-limiter <SUB-COMMAND>

Status

Der Unterbefehl status gibt den Betriebsstatus des Memory Limiters an:

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 Limiter Prozesse aktiv überwacht.
visibleMem und notVisibleMem
Geben die berechneten absoluten Speicherlimits für jeden Status an.
events
Die Anzahl der Male, die ein Prozess sein Limit überschritten hat.
processes
Die Anzahl der überwachten Prozesse.

Ignorieren

Mit dem Unterbefehl ignore können Sie eine UID oder alle Prozesse vorübergehend von der Begrenzung ausschließen. Diese Aktion ist nützlich für Leistungstests oder wenn eine bestimmte App ihre Limits überschreiten darf.

adb shell am memory-limiter ignore 10087  // Ignore a specific UID
adb shell am memory-limiter ignore all    // Ignore all processes (effectively disables limiting)
adb shell am memory-limiter ignore none   // Resume normal operation

Manuell

Mit dem Unterbefehl manual können Sie die berechneten Limits für einen bestimmten Prozess (nach Prozess-ID oder PID) mit einem benutzerdefinierten absoluten Wert in Megabyte (MB) überschreiben:

adb shell am memory-limiter manual 1234 1024   // Set a 1024 MB limit for PID 1234
adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234

Manuelle Überschreibungen gelten nur für den Lebenszyklus des Prozesses. Wenn der Prozess neu gestartet wird, werden die Standardlimits basierend auf seinem Status wiederhergestellt.