Demon Low Memory Killer

Proces demona lmkd (low memory killer) monitoruje stan pamięci działającego systemu Android i reaguje na duże obciążenie pamięci, zamykając najmniej istotne procesy, aby utrzymać wydajność systemu na akceptowalnym poziomie.

Obciążenie pamięci

System Android, w którym równolegle działa wiele procesów, może napotkać sytuacje, w których pamięć systemowa jest wyczerpana, a procesy wymagające więcej pamięci doświadczają zauważalnych opóźnień. Obciążenie pamięci, czyli stan, w którym systemowi brakuje pamięci, wymaga od Androida zwolnienia pamięci (aby zmniejszyć obciążenie) przez ograniczanie lub zamykanie nieistotnych procesów, proszenie procesów o zwolnienie niekrytycznych zasobów w pamięci podręcznej itp.

W przeszłości Android monitorował obciążenie pamięci systemowej za pomocą sterownika LMK (low memory killer) w jądrze, czyli sztywnego mechanizmu, który zależy od zakodowanych na stałe wartości. Od jądra 4.12 sterownik LMK jest usuwany z jądra upstream, a lmkd w przestrzeni użytkownika wykonuje zadania monitorowania pamięci i zamykania procesów.

Informacje o wstrzymaniu z powodu obciążenia

Android 10 i nowsze wersje obsługują nowy tryb lmkd, który do wykrywania obciążenia pamięci używa monitorów PSI (pressure stall information) jądra. Zestaw poprawek PSI w jądrze upstream (przeniesiony do jąder 4.9 i 4.14) mierzy czas, przez jaki zadania są opóźniane z powodu niedoboru pamięci. Ponieważ te opóźnienia bezpośrednio wpływają na wygodę użytkownika, stanowią wygodną metrykę do określania stopnia obciążenia pamięci. Jądro upstream zawiera też monitory PSI, które umożliwiają procesom w przestrzeni użytkownika z uprawnieniami (takim jak lmkd) określanie progów tych opóźnień i subskrybowanie zdarzeń z jądra, gdy próg zostanie przekroczony.

Monitory PSI a sygnały vmpressure

Ponieważ sygnały vmpressure (generowane przez jądro do wykrywania obciążenia pamięci i używane przez lmkd) często zawierają liczne fałszywe alarmy, lmkd musi przeprowadzać filtrowanie, aby określić, czy pamięć jest rzeczywiście obciążona. Powoduje to niepotrzebne wybudzanie lmkd i używanie dodatkowych zasobów obliczeniowych. Używanie monitorów PSI zapewnia dokładniejsze wykrywanie obciążenia pamięci i minimalizuje obciążenie związane z filtrowaniem.

Używanie monitorów PSI

Aby używać monitorów PSI zamiast zdarzeń vmpressure, skonfiguruj właściwość ro.lmk.use_psi. Domyślnie jest ustawiona wartość true, co sprawia, że monitory PSI są domyślnym mechanizmem wykrywania obciążenia pamięci dla lmkd. Ponieważ monitory PSI wymagają obsługi jądra, jądro musi zawierać poprawki PSI i być skompilowane z włączoną obsługą PSI (CONFIG_PSI=y).

Wady sterownika LMK w jądrze

Android wycofuje sterownik LMK z powodu wielu problemów, m.in.:

  • Urządzenia z małą ilością pamięci RAM musiały być agresywnie dostrajane, a mimo to działały słabo w przypadku obciążeń z dużą aktywną pamięcią podręczną stron. Niska wydajność powodowała thrashing i brak zamknięć.
  • Sterownik jądra LMK opierał się na limitach wolnej pamięci, bez skalowania na podstawie obciążenia pamięci.
  • Ze względu na sztywność projektu partnerzy często dostosowywali sterownik, aby działał na ich urządzeniach.
  • Sterownik LMK był podłączony do interfejsu API slab shrinker, który nie został zaprojektowany do wykonywania intensywnych operacji, takich jak wyszukiwanie celów i ich zamykanie, co spowalniało proces vmscan.

Lmkd w przestrzeni użytkownika

lmkd w przestrzeni użytkownika implementuje tę samą funkcję co sterownik w jądrze, ale do wykrywania i szacowania obciążenia pamięci używa istniejących mechanizmów jądra. Mechanizmy te obejmują używanie generowanych przez jądro zdarzeń vmpressure lub monitorów PSI (pressure stall information) do otrzymywania powiadomień o poziomach obciążenia pamięci oraz używanie funkcji cgroup pamięci do ograniczania zasobów pamięci przydzielonych każdemu procesowi na podstawie jego ważności.

Używanie lmkd w przestrzeni użytkownika w Androidzie 10

W Androidzie 9 i nowszych wersjach lmkd w przestrzeni użytkownika aktywuje się, jeśli nie zostanie wykryty sterownik LMK w jądrze. Ponieważ lmkd w przestrzeni użytkownika wymaga obsługi cgroup pamięci przez jądro, jądro musi być skompilowane z tymi ustawieniami konfiguracji:

CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

Strategie zamykania

lmkd w przestrzeni użytkownika obsługuje strategie zamykania oparte na zdarzeniach vmpressure lub monitorach PSI, ich ważności i innych wskazówkach, takich jak wykorzystanie pamięci wymiany. Strategie zamykania różnią się w zależności od tego, czy urządzenie ma mało pamięci, czy jest wydajne:

  • Na urządzeniach z małą ilością pamięci system powinien tolerować większe obciążenie pamięci jako normalny tryb działania.
  • Na urządzeniach o wysokiej wydajności obciążenie pamięci należy traktować jako sytuację nienormalną i naprawić ją, zanim wpłynie na ogólną wydajność.

Strategię zamykania możesz skonfigurować za pomocą właściwości ro.config.low_ram.

lmkd w przestrzeni użytkownika obsługuje też tryb starszy, w którym podejmuje decyzje o zamknięciu, stosując te same strategie co sterownik LMK w jądrze (czyli progi wolnej pamięci i pamięci podręcznej plików). Aby włączyć tryb starszy, ustaw właściwość ro.lmk.use_minfree_levels na true.

Konfigurowanie lmkd

Skonfiguruj lmkd na konkretnym urządzeniu za pomocą tych właściwości.

Właściwość Użyj Domyślny
ro.config.low_ram Określ, czy urządzenie ma mało pamięci RAM, czy jest wydajne. false
ro.lmk.use_psi Używaj monitorów PSI (zamiast zdarzeń vmpressure). true
ro.lmk.use_minfree_levels Używaj progów wolnej pamięci i pamięci podręcznej plików do podejmowania decyzji o zamknięciu procesu (czyli dopasuj funkcję do sterownika LMK w jądrze ). false
ro.lmk.low Minimalny wynik oom_adj dla procesów, które można zamknąć przy niskim poziomie vmpressure level. 1001
(wyłączone)
ro.lmk.medium Minimalny wynik oom_adj dla procesów, które można zamknąć przy średnim poziomie vmpressure. 800
(usługi w pamięci podręcznej lub nieistotne)
ro.lmk.critical Minimalny wynik oom_adj dla procesów, które można zamknąć przy krytycznym poziomie vmpressure. 0
(dowolny proces)
ro.lmk.critical_upgrade Włącz uaktualnienie do poziomu krytycznego. false
ro.lmk.upgrade_pressure Maksymalne mem_pressure, przy którym poziom jest podnoszony ponieważ system zbyt często korzysta z pamięci wymiany. 100
(wyłączone)
ro.lmk.downgrade_pressure Minimalne mem_pressure, przy którym zdarzenie vmpressure jest ignorowane, ponieważ nadal dostępna jest wystarczająca ilość wolnej pamięci. 100
(wyłączone)
ro.lmk.kill_heaviest_task Zamknij najcięższe kwalifikujące się zadanie (najlepsza decyzja) zamiast dowolnego kwalifikującego się zadania (szybka decyzja). false
ro.lmk.kill_timeout_ms Czas w milisekundach po zamknięciu, po którym nie będzie wykonywane żadne dodatkowe zamknięcie. 0
(wyłączone)
ro.lmk.debug Włącz dzienniki debugowania lmkd. false

Przykładowa konfiguracja urządzenia:

PRODUCT_PROPERTY_OVERRIDES += \
    ro.lmk.low=1001 \
    ro.lmk.medium=800 \
    ro.lmk.critical=0 \
    ro.lmk.critical_upgrade=false \
    ro.lmk.upgrade_pressure=100 \
    ro.lmk.downgrade_pressure=100 \
    ro.lmk.kill_heaviest_task=true

Lmkd w przestrzeni użytkownika w Androidzie 11

Android 11 ulepsza lmkd przez wprowadzenie nowej strategii zamykania. Strategia zamykania używa mechanizmu PSI do wykrywania obciążenia pamięci, który został wprowadzony w Androidzie 10. lmkd w Androidzie 11 uwzględnia poziomy wykorzystania zasobów pamięci i thrashing, aby zapobiegać niedoborowi pamięci i pogorszeniu wydajności. Ta strategia zamykania zastępuje poprzednie strategie i może być używana zarówno na urządzeniach o wysokiej wydajności, jak i na urządzeniach z małą ilością pamięci RAM (Android Go).

Wymagania dotyczące jądra

W przypadku urządzeń z Androidem 11 lmkd wymaga tych funkcji jądra:

  • Dołącz poprawki PSI i włącz PSI (przeniesione do wspólnych jąder Androida 4.9, 4.14 i 4.19).
  • Dołącz poprawki obsługi PIDFD (przeniesione do wspólnych jąder Androida 4.9, 4.14 i 4.19).
  • W przypadku urządzeń z małą ilością pamięci RAM dołącz cgroup pamięci.

Jądro musi być skompilowane z tymi ustawieniami konfiguracji:

CONFIG_PSI=y

Konfigurowanie lmkd w Androidzie 11

Strategia zamykania pamięci w Androidzie 11 obsługuje wymienione poniżej ustawienia i wartości domyślne. Te funkcje działają zarówno na urządzeniach o wysokiej wydajności, jak i na urządzeniach z małą ilością pamięci RAM.

Właściwość Użyj Domyślny
Wysoka wydajność Mała ilość pamięci RAM
ro.lmk.psi_partial_stall_ms Częściowy próg wstrzymania PSI w milisekundach, który powoduje wyświetlenie powiadomienia o małej ilości pamięci. Jeśli urządzenie otrzymuje powiadomienia o obciążeniu pamięci zbyt późno, zmniejsz tę wartość, aby powiadomienia były wyświetlane wcześniej. Jeśli powiadomienia o obciążeniu pamięci są wyświetlane niepotrzebnie, zwiększ tę wartość, aby urządzenie było mniej wrażliwe na szumy. 70 200
ro.lmk.psi_complete_stall_ms Pełny próg wstrzymania PSI w milisekundach, który powoduje wyświetlenie powiadomienia o krytycznym obciążeniu pamięci. Jeśli urządzenie otrzymuje powiadomienia o krytycznym obciążeniu pamięci zbyt późno, zmniejsz tę wartość, aby powiadomienia były wyświetlane wcześniej. Jeśli powiadomienia o krytycznym obciążeniu pamięci są wyświetlane niepotrzebnie, zwiększ tę wartość, aby urządzenie było mniej wrażliwe na szumy. 700
ro.lmk.thrashing_limit Maksymalna liczba błędów ponownego wczytania zestawu roboczego jako procent łącznego rozmiaru pamięci podręcznej stron. Błędy ponownego wczytania zestawu roboczego powyżej tej wartości oznaczają że system wykonuje thrashing pamięci podręcznej stron. Jeśli wydajność urządzenia jest obniżona podczas obciążenia pamięci, zmniejsz tę wartość, aby ograniczyć thrashing. Jeśli wydajność urządzenia jest niepotrzebnie obniżana z powodu thrashingu, zwiększ tę wartość, aby zezwolić na większy thrashing. 100 30
ro.lmk.thrashing_limit_decay Spadek progu thrashingu wyrażony jako procent pierwotnego progu używanego do obniżania progu, gdy system nie odzyskuje sprawności nawet po zamknięciu. Jeśli ciągły thrashing powoduje niepotrzebne zamknięcia, zmniejsz tę wartość. Jeśli reakcja na ciągły thrashing po zamknięciu jest zbyt wolna, zwiększ tę wartość. 10 50
ro.lmk.swap_util_max Maksymalna ilość pamięci wymiany jako procent łącznej ilości pamięci wymiany. Gdy ilość pamięci wymiany przekroczy ten limit, oznacza to, że system wykorzystał większość pamięci wymiany i nadal jest obciążony. Może się to zdarzyć, gdy alokacje niepodlegające wymianie generują obciążenie pamięci którego nie można zmniejszyć przez wymianę, ponieważ większość pamięci wymiany jest już wymieniona. Wartość domyślna to 100, co powoduje wyłączenie tego sprawdzania. Jeśli wydajność urządzenia jest obniżona podczas obciążenia pamięci, gdy wykorzystanie pamięci wymiany jest wysokie, a poziom wolnej pamięci wymiany nie spada do ro.lmk.swap_free_low_percentage, zmniejsz tę wartość, aby ograniczyć wykorzystanie pamięci wymiany. 100 100

Te stare ustawienia też działają z nową strategią zamykania.

Właściwość Użyj Domyślny
Wysoka wydajność Mała ilość pamięci RAM
ro.lmk.swap_free_low_percentage Poziom wolnej pamięci wymiany jako procent łącznej ilości pamięci wymiany. `lmkd` używa tej wartości jako progu, po przekroczeniu którego system jest uznawany za mający niedobór pamięci wymiany. Jeśli `lmkd` zamyka procesy, gdy w pamięci wymiany jest zbyt dużo miejsca, zmniejsz ten procent. Jeśli `lmkd` zamyka procesy zbyt późno, co powoduje zamknięcia OOM procesów, zwiększ ten procent. 20 10
ro.lmk.debug Włącza dzienniki debugowania `lmkd`. Włącz debugowanie podczas dostrajania. false