Auf dieser Seite wird die Interaktion zwischen kswapd und lmkd unter Android beschrieben und gezeigt, wie sie zusammenarbeiten, um die Speicherauslastung zu verwalten.
Hintergrund
Android verwendet zwei primäre Mechanismen, um Arbeitsspeicher freizugeben, wenn der Speicher knapp wird:
kswapd: Ein Standard-Linux-Kernel-Daemon, der Seiten freigibt, indem er saubere dateibasierte Seiten entfernt oder anonyme Seiten in ZRAM auslagert.lmkd: Der Low Memory Killer-Daemon im Android-Nutzerbereich. Wennkswapddie Speicheranforderungen nicht mehr erfüllen kann und die Systemauslastung steigt (wie durch PSI signalisiert), greiftlmkdein, um Prozesse zu beenden und größere Speicherbereiche freizugeben.
kswapd und lmkd sind zwei sich ergänzende Systeme zur Speicherfreigabe. Sie arbeiten auf dasselbe Ziel hin (freien Speicherplatz schaffen), aber mit unterschiedlichen Mitteln. Vergleich:
- Wiederherstellbar im Vergleich zu destruktiv:
kswapdverwirft Speicherseiten, die aus dem Speicher zurückgelesen werden können, oder tauscht Speicherseiten mit anderen Speicherpartitionen oder Speichern aus, die bei Bedarf zurückgelesen werden können.lmkdbeendet Prozesse, wodurch der nichtflüchtige Zustand zerstört wird. - Bandbreite zurückgewinnen:
kswapdbenötigt mehr CPU-Leistung alslmkd, um eine bestimmte Menge an verfügbarem Arbeitsspeicher zurückzugewinnen, und es dauert länger, diesen Arbeitsspeicher zurückzugewinnen.lmkdkann große Mengen an Arbeitsspeicher in kurzen Intervallen mit relativ geringem Aufwand freigeben. Beachten Sie, dass sich die CPU-Leistung auf Mobilgeräten nicht nur auf die Latenz, sondern auch auf die Akkuleistung auswirkt.
OOM-Score-Buckets
Android weist Prozessen basierend auf ihrer Bedeutung für den Nutzer eine OOM-Anpassung (Out-Of-Memory) (oom_score_adj) zu. Prozesse mit höheren Werten werden zuerst beendet.
Häufige Buckets sind:
- Im Cache gespeichert (Wert >= 900): Prozesse, die derzeit nicht benötigt werden und für einen schnelleren Neustart aufbewahrt werden.
- Dienst (Wert 500): Prozesse, die gestartete Dienste hosten.
- Dienst im Vordergrund (Wert 200): Prozesse, die Dienste im Vordergrund hosten.
- Vordergrund (Wert 0): Die App, mit der der Nutzer gerade interagiert.
Im Detail: PSI- und LMKD-Triggering
Was ist PSI?
Pressure Stall Information (PSI) ist ein Linux-Kernel-Feature, das eine kanonische Methode zum Messen von Ressourcenengpässen bietet. Es wird die Zeit erfasst, um die Aufgaben aufgrund von CPU-, Speicher- und E/A-Engpässen verzögert werden.
Memory-PSI: „some“ im Vergleich zu „full“
Der PSI für den Arbeitsspeicher erfasst insbesondere Verzögerungen, die durch Arbeitsspeichermangel verursacht werden (z.B. Warten auf die Freigabe von Seiten oder Seitenfehler). Es werden zwei Messwerte bereitgestellt:
some: Der Prozentsatz der Zeit, in der mindestens eine Aufgabe aufgrund von Arbeitsspeichermangel blockiert war. Dies weist darauf hin, dass sich der Speichermangel auf die Leistung auswirkt, aber einige Aufgaben noch ausgeführt werden.full: Der Prozentsatz der Zeit, in der alle nicht inaktiven Aufgaben gleichzeitig blockiert waren. Dies weist auf einen kritischen Zustand hin, in dem das System vollständig blockiert ist und auf Speicher wartet.
Wie PSI LMKD auslöst
Der Userspace-Daemon lmkd registriert sich beim Kernel, um Benachrichtigungen zu erhalten, wenn PSI-Schwellenwerte überschritten werden (durch Überwachung von /proc/pressure/memory). Wenn das System unter hohem Speicherdruck steht (z.B. full-Stall, der eine konfigurierte Dauer überschreitet), weckt der Kernel lmkd auf. lmkd wertet dann den Status des Systems aus und entscheidet anhand der OOM-Werte und der Größe, ob und welche Prozesse beendet werden sollen.
LMKD-Abstimmung
Das LMKD-Verhalten kann mithilfe von Systemeigenschaften angepasst werden.
Abstimmungswerte auf einem Gerät finden
Die aktuellen LMKD-Abstimmungswerte auf einem verbundenen Gerät können Sie mit folgendem Befehl abrufen:
adb shell getprop | grep lmk
Allgemeine LMKD-Eigenschaften
Hier sind einige der wichtigsten Eigenschaften, die zum Optimieren von LMKD verwendet werden. Eine vollständige Liste finden Sie in der Android-Dokumentation:
ro.lmk.use_psi: Auftruesetzen, um PSI-Monitore für die Erkennung von Speicherdruck zu verwenden (Standardeinstellung unter Android 10 und höher).ro.lmk.psi_partial_stall_ms: Der Schwellenwert für die Dauer eines (teilweisen)some-Stalls in Millisekunden, um einen Kill auszulösen (Standard: 70 ms auf normalen Geräten, 200 ms auf Geräten mit wenig RAM).ro.lmk.psi_complete_stall_ms: Der Schwellenwert für die Dauer desfull-Stalls in Millisekunden, um einen Kill auszulösen (Standardwert: 700 ms).ro.lmk.kill_heaviest_task: Wenntrue, beendet LMKD den Prozess, der den meisten Arbeitsspeicher unter den Kandidaten im selben OOM-Bucket verwendet.
Relevante Abstimmungswerte auf dem Testgerät (Google Pixel 8 Pro)
(Hinweis: Typische Standardwerte für High-End-Geräte mit Android 15)
ro.lmk.use_psi:truero.lmk.psi_partial_stall_ms:70(typisch)ro.lmk.psi_complete_stall_ms:700(typisch)ro.lmk.kill_heaviest_task:true
ZRAM- und Swap-Überprüfung
Die ZRAM-Kapazität hat einen großen Einfluss darauf, wie schnell der Swap-Speicher voll wird und LMKD-Kills auslöst. Mit den folgenden Befehlen können Sie den ZRAM- und Swap-Status auf dem Gerät prüfen:
# Check ZRAM sizing and usage
adb shell free -m
# Check swap partition details
adb shell cat /proc/swaps
Praktische Übung: kswapd und lmkd auslösen
In dieser Übung verwenden Sie die Beispiel-App KswapdLmkdLab, um Prozesse in verschiedenen OOM-Buckets zu erstellen und dann im Hauptprozess Arbeitsspeicher zu belegen, um kswapd und lmkd auszulösen.
Die App ist so konfiguriert, dass sie automatisch Unterprozesse erstellt und Arbeitsspeicher zuweist, um sie zu attraktiven Zielen für LMKD zu machen.
1. Unterprozesse erstellen
Die App startet beim Start automatisch die folgenden Unterprozesse:
- Im Cache gespeicherter Prozess (oom_score_adj >= 900)
- Dienstprozess (oom_score_adj 500)
- Dienst im Vordergrund (oom_score_adj 200)
Prüfen Sie die Prozesse und ihre OOM-Scores mit ADB:
adb shell dumpsys activity processes | grep com.android.kswapdlmkdlab
Beispiel für ein Ausgabe-Snippet:
Proc # 0: fg T/A/TOP LCMNFUATI t: 0 18959:com.android.kswapdlmkdlab/u0a358 (top-activity)
Proc # 1: prcp b/ /FGS -------TI t: 0 19050:com.android.kswapdlmkdlab:foreground/u0a358 (fg-service)
Proc # 2: svc b/ /SVC -------TI t: 0 19077:com.android.kswapdlmkdlab:service/u0a358 (started-services)
Proc # 3: cch b/ /CACC -------TI t: 0 19014:com.android.kswapdlmkdlab:cached/u0a358 (cch-client-act)
Die Abkürzungen für den Prozessstatus entsprechen Folgendem:
fg: Vordergrund (OOM-Wert 0)prcp: Wahrnehmbar (Dienst im Vordergrund, OOM-Wert 200)svc: Dienst gestartet (OOM-Wert 500)cch: Im Cache gespeicherter Prozess (OOM-Wert 900)
2. Speicherauslastung auslösen
Die App beginnt automatisch damit, Arbeitsspeicher im Hauptprozess zuzuweisen. Damit der Test auf Geräten mit viel RAM effektiv ist, weist die App die Unterprozesse außerdem an, Arbeitsspeicher zuzuweisen (z.B. jeweils 500 MB), damit sie bessere Ziele für den LMKD sind.
Beendigung von Unterprozessen überwachen
Die App überwacht das Beenden dieser Unterprozesse mithilfe des IBinder.linkToDeath-Mechanismus:
- Der Hauptprozess wird mit
bindServicean einen Dienst im Zielprozess gebunden. - Nach dem Herstellen der Verbindung wird das
IBinder-Objekt abgerufen, das den Dienst darstellt. - Sie registriert eine
IBinder.DeathRecipient-Implementierung, indem sielinkToDeathauf dem Binder aufruft. - Wenn der Zielprozess beendet wird (z. B. durch LMKD), ruft das System die Methode
binderDied()auf, sodass die App das Beendigungsereignis asynchron aufzeichnen kann.
Damit diese Bindung die Priorität der Zielprozesse nicht erhöht (was den Zweck der Übung zunichtemachen würde), verwendet die App das Flag Context.BIND_WAIVE_PRIORITY.
Beispiel-App in Aktion
Hier ist ein Screenshot der App nach einem Testlauf auf einem Google Pixel 8 Pro (mit insgesamt etwa 12 GB RAM).
Perfetto-Trace-Analyse
Verwenden Sie die folgende Perfetto-Konfiguration, um die Interaktion zu erfassen, einschließlich Planung, Speicherzähler, LMK-Kills und PSI (Pressure Stall Information).
Perfetto-Konfiguration
Erfasse die Interaktion, indem du Perfetto mit Planung, Speicherzählern, LMK-Kills und PSI (Pressure Stall Information) ausführst:
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/kswapd_lmkd.perfetto-trace <<EOF
buffers: { size_kb: 131072 }
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "lowmemorykiller/lowmemory_kill"
ftrace_events: "vmscan/mm_vmscan_kswapd_wake"
ftrace_events: "vmscan/mm_vmscan_kswapd_sleep"
ftrace_events: "psi/psi_event"
}
}
}
duration_ms: 180000
EOF
Trace prüfen
Der folgende Screenshot stammt aus einem Trace, der den Zeitraum kurz vor dem Beenden des Prozesses :cached (am ehesten beendbar) bis zum Beenden des Prozesses :foreground (am wenigsten beendbar) zeigt.
Sie sehen, dass MemFree bereits ziemlich niedrig ist und ständig unter Druck steht.
kswapd arbeitet ständig und entlastet den Druck, indem es mem.rss.file Seiten löscht und mem.rss.anon bis mem.swap aus allen kswapdlmkdlab-Prozessen (dem Hauptprozess und den Unterprozessen) und wahrscheinlich aus anderen Prozessen, die in diesem Screenshot nicht zu sehen sind, verschiebt. Gleichzeitig weist die kswapdlmkdlab-Anwendung weiterhin Arbeitsspeicher zu, wodurch der Druck schneller steigt, als kswapd ihn freigeben kann.
Wenn der Druck zu hoch wird, was sich daran zeigt, dass psi.mem über einen längeren Zeitraum hoch ist, wird lmkd aktiviert und beendet einige Prozesse. Sie können sehen, dass lmkd die OOM-Werte berücksichtigt und zuerst kswapdlmkdlab:cached, dann :service und dann :foreground beendet.
Sehen Sie sich die Zeitleiste der Ereignisse an und denken Sie über Folgendes nach:
- So ergänzen sich
kswapdundlmkd:kswapdreagiert zuerst auf Speicherdruck, aberlmkdhilft, wenn die Bandbreite für die Rückgewinnung vonkswapdnicht den Zuweisungsbedarf deckt. - Die Rate, mit der Arbeitsspeicher freigegeben wird: Beobachten Sie die CPU-Zeit, die in
kswapdverbracht wird, und den Arbeitsspeicher, der durch das Löschen vonmem.rss.fileoder das Auslagern vonmem.rss.anoninmem.swapfreigegeben wird. Vergleichen Sie dies mit den kurzen Aktivitätsphasen inlmkd, die dazu führen, dass große Mengen an privatem Prozessspeicher freigegeben werden.
Abfragen
Führen Sie diese Abfragen in der Perfetto-Benutzeroberfläche (Query SQL) aus, um Daten aus Ihrem Trace zu extrahieren.
1. kswapd-Aktivität
Führen Sie folgenden Befehl aus, um herauszufinden, wann der Swap-Daemon des Kernels aktiv war und ausgeführt wurde:
SELECT ts, dur, state
FROM thread_state
JOIN thread USING (utid)
WHERE thread.name LIKE 'kswapd%'
AND state = 'Running'
LIMIT 5;
Beispielausgabe:
| ts | dur | state |
|---|---|---|
| 30317633052969 | 159180 | Wird ausgeführt |
| 30317633398347 | 3999186 | Wird ausgeführt |
| 30317638448111 | 1847941 | Wird ausgeführt |
| 30317641455354 | 1031006 | Wird ausgeführt |
| 30317643272615 | 888387 | Wird ausgeführt |
2. LMKD-Beendigungen
LMKD schreibt Low-Memory-Kills in die Tabelle instant. So fragen Sie alle LMKD-Beendigungen ab:
SELECT ts, name
FROM instant
WHERE name LIKE 'lmk,%'
ORDER BY ts ASC;
Das Format des Ereignisnamens ist:
lmk,<pid>,<kill_reason>,<oom_score_adj>,<min_oom_score>,<max_thrashing>.
kill_reason: Eine Ganzzahl, die angibt, warumlmkdden Prozess beendet hat (definiert insystem/memory/lmkd/statslog.h):0:PRESSURE_AFTER_KILL1:NOT_RESPONDING2:LOW_SWAP_AND_THRASHING(ZRAM-Swap-Speicher ist extrem niedrig und es ist zu Thrashing gekommen)3:LOW_MEM_AND_SWAP(Das System hat wenig verfügbaren Arbeitsspeicher und ZRAM-Swap)4:LOW_MEM_AND_THRASHING5:DIRECT_RECL_AND_THRASHING6:LOW_MEM_AND_SWAP_UTIL7:LOW_FILECACHE_AFTER_THRASHING8:LOW_MEM9:DIRECT_RECL_STUCK
max_thrashing: Der maximale Prozentsatz des Seiten-Thrashings zum Zeitpunkt des Beendens.
Beispielausgabe:
| ts | Name |
|---|---|
| 30326148679967 | lmk,2308,3,900,201,0 |
| 30330439763710 | lmk,2309,3,500,201,0 |
| 30334985999837 | lmk,2310,2,200,0,307 |
Visualisierung
Zur Visualisierung der Daten können Sie das folgende gnuplot-Skript verwenden, um den verfügbaren Systemspeicher, die kswapd-Aktivität, die lmkd-Kills und den PSI in einer einzigen Zeitreihe darzustellen. Das Script geht davon aus, dass es im obersten Verzeichnis des Android-Builds ausgeführt wird.
Beispiel für ein Ausgabediagramm
Das folgende Diagramm wurde mit dem vorherigen Script und Beispieldaten generiert, die für den Testlauf repräsentativ sind.
Erläuterung des Diagramms:
Dieses Diagramm veranschaulicht die dynamische Interaktion zwischen den verschiedenen Komponenten der Speicherverwaltung während des Stresstests:
- Abfall des verfügbaren Arbeitsspeichers: Da die Haupt-App Arbeitsspeicher zuweist, nimmt der verfügbare Arbeitsspeicher des Systems (lila Linie) kontinuierlich ab.
- kswapd-Aktivierung: Wenn der verfügbare Arbeitsspeicher unter bestimmte Grenzwerte fällt, wird
kswapdaktiviert (blaue Impulse), um Arbeitsspeicher durch Auslagerung in ZRAM zurückzugewinnen. Dies ist an den unregelmäßigen Spitzen zu erkennen. - PSI-Spitzen: Da
kswapdmit der hohen Zuweisungsrate nicht Schritt halten kann, werden Threads bei der Arbeitsspeicherzuweisung blockiert, was zu Spitzen bei den PSI (Pressure Stall Information) (grüne Linie) führt. - LMKD-Beendigungen: Wenn der PSI kritische Werte erreicht (z.B. den Schwellenwert
ro.lmk.psi_complete_stall_msüberschreitet) und der verfügbare Arbeitsspeicher extrem niedrig ist, greiftlmkdein und beendet Prozesse mit niedriger Priorität (orangefarbene Impulse bei 30 und 45), um große Mengen an Arbeitsspeicher wieder verfügbar zu machen. Unmittelbar nach dem Beenden steigt der verfügbare Arbeitsspeicher und der PSI sinkt, was die Wirksamkeit des Beendens bei der Druckentlastung zeigt. ________________________________________________________________________________
← Speicherfreigabe: Entfernung und Auslagerung | ↑ Nach oben | Speicherseiten an das Betriebssystem zurückgeben →