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 |
|