Demon Low Memory Killer

Demon (lmkd) Androida do zabijania procesów w przypadku niskiej ilości pamięci monitoruje stan pamięci uruchomionego systemu Androida i reaguje na wysokie obciążenie pamięci, zabijając procesy o najmniejszym znaczeniu, aby zachować akceptowalny poziom wydajności systemu.

Informacje o obciążeniu pamięci

System Android, który równolegle wykonuje wiele procesów, może napotkać sytuacje, gdy wyczerpuje się pamięć systemowa, a procesy wymagające więcej pamięci mogą odczuwać zauważalne opóźnienia. Napięcie pamięci to stan, w którym systemowi brakuje pamięci. Aby zmniejszyć to napięcie, Android musi zwolnić pamięć, na przykład przez ograniczenie lub zamykanie nieistotnych procesów, żądanie zwolnienia nieistotnych zasobów z pamięci podręcznej itp.

Wcześniej Android monitorował obciążenie pamięci systemowej za pomocą sterownika LMK (low memory killer) działającego w rdzeniu. Jest to sztywny mechanizm, który zależy od zakodowanych na stałe wartości. Od jądra 4.12 sterownik LMK został usunięty z upstreamowego jądra, a zadania monitorowania pamięci i zabijania procesów wykonuje przestrzeń użytkownika lmkd.

Informacje o zatrzymaniu spowodowanym przez spadek ciśnienia

Android 10 i nowsze wersje obsługują nowy tryb lmkd, który wykorzystuje monitory PSI (pressure stall information) jądra do wykrywania nacisku pamięci. Zestaw poprawek PSI w rdzeniach upstream (przeniesionych do wersji 4.9 i 4.14) mierzy czas opóźnienia zadań spowodowany brakiem pamięci. Te opóźnienia mają bezpośredni wpływ na wrażenia użytkownika, dlatego stanowią wygodny wskaźnik do określania stopnia obciążenia pamięci. Kernel upstream zawiera też monitory PSI, które umożliwiają uprzywilejowanym procesom w przestrzeni użytkownika (takim jak lmkd) określanie wartości progowych opóźnień i subskrybowanie zdarzeń z jądra w przypadku przekroczenia wartości progowych.

Różnica między monitorowaniem PSI a vmpressure

Ponieważ sygnały vmpressure (generowane przez jądro do wykrywania ciśnienia w pamięci i używane przez lmkd) często zawierają wiele fałszywych wyników pozytywnych, lmkd musi przeprowadzić filtrowanie, aby określić, czy pamięć jest pod prawdziwym ciśnieniem. Powoduje to niepotrzebne lmkd budzenia i używanie dodatkowych zasobów obliczeniowych. Korzystanie z monitorowania PSI skutkuje dokładniejszym wykrywaniem ciśnienia pamięci i minimalizacją narzutu związanego z filtrowaniem.

Korzystanie z monitorów PSI

Aby zamiast zdarzeń vmpressure używać monitorów PSI, skonfiguruj właściwość ro.lmk.use_psi. Wartość domyślna to true, co powoduje, że PSI monitoruje domyślny mechanizm wykrywania nacisku pamięci dla lmkd. Monitory PSI wymagają obsługi przez jądro, dlatego jądro musi zawierać poprawki PSI i być skompilowane z włączoną obsługą PSI (CONFIG_PSI=y).

Wady sterownika LMK w rdzeniu

Android wycofuje sterownik LMK z powodu kilku problemów, w tym:

  • Urządzenia z małą ilością pamięci RAM wymagały agresywnego dostosowania, a nawet wtedy pracowały słabo w przypadku zadań z dużą aktywną pamięcią podręczną pagecache obsługiwaną przez pliki. Złe wyniki spowodowały utratę kontroli i brak zabójstw.
  • Sterownik jądra LMK korzystał z limitów 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 do kompresji bloków, który nie był przeznaczony do wykonywania czasochłonnych operacji, takich jak wyszukiwanie celów i ich zabijanie, co spowalniało proces vmscan.

Userspace lmkd

Przestrzeń użytkownika lmkd realizuje te same funkcje co sterownik w jądrze, ale do wykrywania i szacowania obciążenia pamięci używa istniejących mechanizmów jądra. Takie mechanizmy obejmują korzystanie ze zdarzeń generowanych przez jądro vmpressure lub monitorów informacji o ciśnieniu (PSI) w celu otrzymywania powiadomień o poziomie ciśnienia w pamięci. Obejmują one też korzystanie z funkcji cgroup pamięci w celu ograniczenia zasobów pamięci przydzielonych do każdego procesu na podstawie jego ważności.

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

W Androidzie 9 i nowszych przestrzeń użytkownika lmkd jest aktywowana, jeśli nie wykryto sterownika LMK w rdzeniu. Ponieważ przestrzeń użytkownika lmkd wymaga obsługi cgroup przez jądro, jądro musi zostać skompilowane z tymi ustawieniami konfiguracji:

CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

Zabijanie strategii

Userspace lmkd obsługuje strategie zabijania na podstawie zdarzeń vmpressure lub monitorów PSI, ich ważności i innych wskazówek, takich jak wykorzystanie swapa. Strategie zabijania różnią się w zależności od tego, czy urządzenie ma mało pamięci czy dużą wydajność:

  • Na urządzeniach z małą ilością pamięci system powinien tolerować wyższe wykorzystanie pamięci jako normalny tryb działania.
  • Na urządzeniach o wysokiej wydajności wykorzystanie pamięci należy traktować jako nieprawidłową sytuację i usuwać, zanim wpłynie na ogólną wydajność.

Strategię wykluczania możesz skonfigurować za pomocą usługi ro.config.low_ram.

Userspace lmkd obsługuje też tryb starszy, w którym podejmuje decyzje o zatrzymaniu procesu, używając tych samych strategii co sterownik LMK w rdzeniu (czyli progów wolnej pamięci i pliku pamięci podręcznej). Aby włączyć tryb starszy, ustaw właściwość ro.lmk.use_minfree_levels na true.

Konfigurowanie lmkd

Skonfiguruj lmkd na konkretnym urządzeniu, używając tych właściwości.

Właściwość Użyj Domyślny
ro.config.low_ram Określ, czy urządzenie ma małą ilość pamięci RAM, czy jest to urządzenie o wysokiej wydajności. 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 pliku pamięci podręcznej do podejmowania decyzji o zakończeniu procesu (czyli dopasowywania funkcji do jądra sterownika LMK). false
ro.lmk.low Minimalna wartość oom_adj procesów kwalifikujących się do zabijania na poziomie vmpressure. 1001
(wyłączona)
ro.lmk.medium Minimalny wynik oom_adj procesów kwalifikujących się do zabijania na średnim poziomie vmpressure. 800
(przechowywane w pamięci podręcznej lub nieistotne usługi)
ro.lmk.critical Minimalny wynik oom_adj procesów kwalifikujących się do zabijania na poziomie krytycznym vmpressure. 0
(dowolny proces)
ro.lmk.critical_upgrade Włącz uaktualnienie do poziomu krytycznego. false
ro.lmk.upgrade_pressure Maksymalna liczba mem_pressure, przy której poziom jest ulepszany, ponieważ system zbyt często przełącza się między poziomami. 100
(wyłączona)
ro.lmk.downgrade_pressure Minimalna wartość mem_pressure, przy której zdarzenie vmpressure jest ignorowane, ponieważ nadal jest wystarczająco dużo wolnej pamięci. 100
(wyłączona)
ro.lmk.kill_heaviest_task Zatrzymanie najbardziej obciążającego zadania (najlepsza decyzja) w porównaniu z dowolnym innym zadaniem (szybka decyzja). false
ro.lmk.kill_timeout_ms Czas w milisekundach po zabiciu, po którym nie będzie dokonywane żadne dodatkowe zabicie. 0
(wyłączona)
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, wprowadzając nową strategię zabijania procesów. Strategia zabijania używa mechanizmu PSI do wykrywania nacisku na pamięć, który został wprowadzony w Androidzie 10. lmkd w Androidzie 11 uwzględnia poziomy wykorzystania zasobów pamięci i throttlingu, aby zapobiec niedoborowi pamięci i pogorszeniu wydajności. Ta strategia zastępuje poprzednie strategie i może być stosowana 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 (backporty dostępne w powszechnie używanych jądrach Androida 4.9, 4.14 i 4.19).
  • Dołącz poprawki dotyczące obsługi PIDFD (poprawki dostępne w jądrach wspólnych Androida w wersjach 4.9, 4.14 i 4.19).
  • W przypadku urządzeń z małą ilością pamięci RAM uwzględnij grupy pamięci.

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

CONFIG_PSI=y

Konfigurowanie lmkd w Androidzie 11

Strategia zabijania pamięci w Androidzie 11 obsługuje ustawienia i wartości domyślne wymienione poniżej. 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 Próg częściowego zablokowania PSI (w milisekundach) dla powiadomienia o niskiej pamięci. Jeśli urządzenie otrzymuje powiadomienia o nadmiarze pamięci zbyt późno, zmniejsz tę wartość, aby powiadomienia były wysyłane wcześniej. Jeśli powiadomienia o nadmiarowym ciśnieniu są niepotrzebnie aktywowane, zwiększ tę wartość, aby zmniejszyć wrażliwość urządzenia na hałas. 70 200
ro.lmk.psi_complete_stall_ms Całkowity próg zablokowania PSI (w milisekundach) do wywołania powiadomień o krytycznym wykorzystaniu pamięci. Jeśli urządzenie otrzymuje powiadomienia o krytycznym poziomie pamięci zbyt późno, zmniejsz tę wartość, aby powiadomienia były wysyłane wcześniej. Jeśli powiadomienia o krytycznym obciążeniu pamięci są niepotrzebnie aktywowane, zwiększ tę wartość, aby zmniejszyć wrażliwość urządzenia na hałas. 700
ro.lmk.thrashing_limit Maksymalna liczba refaultów zbioru roboczego w procentach z łącznego rozmiaru pamięci podręcznej obsługiwanej przez plik. Wartości większe od tej wartości oznaczają, że system uważa, że jego pamięć podręczna jest przeciążona. Jeśli wydajność urządzenia jest ograniczona z powodu obciążenia pamięci, zmniejsz tę wartość, aby ograniczyć thrashing. Jeśli wydajność urządzenia jest niepotrzebnie obniżana z powodu thrashingu, zwiększ wartość, aby zezwolić na większą ilość thrashingu. 100 30
ro.lmk.thrashing_limit_decay Zmniejszenie progu tłoczenia wyrażone w procentach pierwotnego progu służącego do obniżenia progu, gdy system nie wraca do normy nawet po zabiciu. Jeśli ciągłe stosowanie funkcji thrashing powoduje niepotrzebne zabijanie procesów, zmniejsz tę wartość. Jeśli reakcja na ciągłe tłoczenie po zabiciu jest zbyt wolna, zwiększ wartość. 10 50
ro.lmk.swap_util_max Maksymalna ilość pamięci podręcznej jako procent łącznej pamięci, którą można zamienić. Gdy ilość pamięci wymiennej przekroczy ten limit, oznacza to, że system przełączył większość pamięci wymiennej i nadal jest obciążony. Może się to zdarzyć, gdy alokacje, których nie można zamienić, powodują wzrost zapotrzebowania na pamięć, którego nie można zmniejszyć przez zamianę, ponieważ większość pamięci, którą można zamienić, jest już zamieniona. Wartość domyślna to 100, co w efekcie wyłącza to sprawdzanie. Jeśli wydajność urządzenia jest niska podczas presji pamięci, a wykorzystanie podmiany jest wysokie i poziom wolnej podmiany nie spada do ro.lmk.swap_free_low_percentage, zmniejsz tę wartość, aby ograniczyć wykorzystanie podmiany. 100 100

W przypadku nowej strategii wygaszania obowiązują te same ustawienia, co w przypadku starszych strategii.

Właściwość Użyj Domyślny
Wysoka wydajność Mała ilość pamięci RAM
ro.lmk.swap_free_low_percentage Poziom bezpłatnego przełączania jako odsetek całkowitego miejsca na wymianę. Funkcja `lmkd` używa tej wartości jako progu, po przekroczeniu którego system uznaje, że brakuje mu miejsca na dysku. Jeśli `lmkd` zabija, gdy jest zbyt dużo miejsca na wymianie, zmniejsz odsetek. Jeśli zabijanie przez `lmkd` następuje zbyt późno, co pozwala na zabijanie przez OOM, zwiększ ten odsetek. 20 10
ro.lmk.debug Spowoduje to włączenie dzienników debugowania `lmkd`. Włącz debugowanie podczas strojenia. false