Android 17 i nowsze wersje zawierają ogranicznik pamięci, czyli usługę systemową, która monitoruje i ogranicza wykorzystanie pamięci przez procesy aplikacji za pomocą cgroup v2 w systemie Linux. Ogranicznik pamięci uniemożliwia poszczególnym aplikacjom zużywanie nieproporcjonalnie dużej ilości pamięci systemowej, co zmniejsza ogólne obciążenie pamięci i zapobiega agresywnemu zamykaniu przez mechanizm low-memory killer (lmkd) krytycznych procesów działających na pierwszym planie i procesów w pamięci podręcznej.
Mechanizm
Ogranicznik pamięci jest zintegrowany z usługą Activity Manager Service (AMS), aby śledzić zdarzenia cyklu życia procesu i zmiany stanu. Ogranicznik pamięci egzekwuje limity pamięci za pomocą hierarchii cgroup v2 jądra systemu Linux.
Aby obsługiwać ogranicznik pamięci, jądro urządzenia musi mieć włączoną cgroup v2 i kontroler memory. Usługa opiera się w szczególności na tych atrybutach:
memory.high- Limit. Po jego przekroczeniu proces jest ograniczany, a jądro próbuje aktywnie odzyskać strony z tego procesu.
memory.swap.max- Ogranicza maksymalną ilość miejsca wymiany (np. ZRAM), jaką może wykorzystać proces.
Wpływ na aplikacje
Ogranicznik pamięci nie ma wpływu na aplikacje, które działają w ramach limitów pamięci.
Gdy aplikacja przekroczy limit memory.high, jądro usunie z pamięci czyste strony aplikacji i przeniesie do pamięci wymiany nieaktywne strony anonimowe, aby utrzymać aplikację w limicie. W wyniku tego usunięcia stron i aktywności związanej z pamięcią wymiany aplikacja może tymczasowo doświadczyć ograniczenia wykonywania, ale będzie nadal działać.
W ekstremalnych sytuacjach, takich jak niekontrolowany wyciek pamięci, gdy aplikacja nadal przydziela pamięć anonimową po wyczerpaniu miejsca wymiany, aplikacja nie może przydzielić pamięci i zostaje zamknięta.
Monitorowanie procesów
Ogranicznik pamięci domyślnie monitoruje procesy aplikacji (UID >= 10000). Procesy podstawowe systemu są wyłączone, aby zachować stabilność systemu.
Ogranicznik pamięci przypisuje limity pamięci na podstawie stanu widoczności procesu:
Widoczne procesy: procesy hostujące interfejs widoczny dla użytkownika (np. aktualnie aktywny ekran lub aktywna interakcja). Ponieważ wyświetlanie interfejsu wymaga większego zestawu roboczego do renderowania i potoków graficznych, widoczne procesy otrzymują większy limit pamięci.
Niewidoczne procesy: procesy wykonujące pracę w tle bez wyświetlania interfejsu (np. usługi działające w tle lub odbiorniki transmisji). Ponieważ nie renderują one komponentów interfejsu, działają w ramach bardziej restrykcyjnego limitu.
W tabeli poniżej przedstawiono mapowanie konkretnych stanów procesu na klasyfikacje limitów pamięci:
| Stan procesu | Limit pamięci |
|---|---|
PERSISTENT | Bez ograniczeń |
PERSISTENT_UI | Bez ograniczeń |
TOP | Widoczne |
BOUND_TOP | Widoczne |
FOREGROUND_SERVICE | Niewidoczne |
BOUND_FOREGROUND_SERVICE | Niewidoczne |
IMPORTANT_FOREGROUND | Widoczne |
IMPORTANT_BACKGROUND | Niewidoczne |
TRANSIENT_BACKGROUND | Niewidoczne |
BACKUP | Niewidoczne |
SERVICE | Niewidoczne |
RECEIVER | Niewidoczne |
TOP_SLEEPING | Widoczne |
HEAVY_WEIGHT | Niewidoczne |
HOME | Niewidoczne |
LAST_ACTIVITY | Niewidoczne |
CACHED_ACTIVITY | W pamięci podręcznej |
CACHED_ACTIVITY_CLIENT | W pamięci podręcznej |
CACHED_RECENT | W pamięci podręcznej |
CACHED_EMPTY | W pamięci podręcznej |
W stanie w pamięci podręcznej procesy są zamrożone, a następnie maksymalnie odzyskane.
Gdy proces przekroczy przypisany limit memory.high, ogranicznik pamięci wykryje to zdarzenie i może wywołać działania diagnostyczne, takie jak przechwycenie profilu pamięci lub zalogowanie anomalii w statsd.
Konfiguracja
W Androidzie 17 i nowszych wersjach ogranicznik pamięci jest konfigurowany za pomocą standardowego pliku konfiguracji platformy w partycji systemowej w lokalizacji /system/etc/memory-limiter-config.xml.
Format XML
Plik konfiguracji jest zgodny ze schematem zdefiniowanym w memory-limiter-config.xsd. Plik definiuje wiele zestawów limitów uporządkowanych według dostępnej pamięci. Usługa wybiera najwyższy pasujący zestaw limitów na podstawie dostępnej pamięci RAM urządzenia. Wszystkie wartości pamięci są podawane w mebibajtach (MiB).
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM boundaries account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
<maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
<maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
<maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- Dodatnia liczba całkowita identyfikująca wersję konfiguracji. Musi to być
1. minimumRequiredMemTotalMinimalna ilość pamięci systemowej
MemTotal(w MiB, włącznie z dolną granicą) wymagana do zastosowania tego zestawu limitów. Usługa porównuje tę wartość z łączną dostępną pamięcią zgłoszoną przez jądro w/proc/meminfo.Informacje o wyłączeniach i granicach pamięci: wartość
MemTotaljądra odzwierciedla fizyczną pamięć DRAM dostępną dla systemu operacyjnego po odjęciu wyłączeń pamięci sprzętu i oprogramowania układowego. Wyłączenia rezerwują pamięć RAM dla wyspecjalizowanych komponentów sprzętowych, takich jak GPU, modem pasma podstawowego, ISP aparatu, bezpieczne środowiska wykonawcze i hipernadzorcy, zwykle zużywając od 500 MiB do ponad 2 GiB pamięci fizycznej.Ponieważ
MemTotaljest mniejsza niż deklarowana fizyczna pamięć DRAM urządzenia,minimumRequiredMemTotalprogi obejmują margines na te wyłączenia. Na przykład konfiguracja przeznaczona dla urządzeń z 16 GB pamięci określa dolną granicę14336(14 GiB), a nie16384(16 GiB), a warstwa dla urządzeń z 12 GB pamięci określa dolną granicę9216(9 GiB). Dzięki temu każde urządzenie pasuje do zamierzonej warstwy niezależnie od różnic w wyłączeniach na poziomie płyty.maximumAllowedMemTotalMaksymalna ilość pamięci systemowej
MemTotal(w MiB, bez górnej granicy) wymagana do zastosowania tego zestawu limitów. JeśliMemTotaljest większa lub równa tej górnej granicy, zestaw limitów nie jest stosowany. W przypadku urządzeń z 18 432 MiB lub więcej albo z mniej niż 3200 MiB ogranicznik pamięci nie stosuje limitów, pozostawiając procesy bez ograniczeń.memVisibleLimit pamięci (
memory.high) w MiB stosowany do widocznych procesów.memNotVisibleLimit pamięci (
memory.high) w MiB stosowany do niewidocznych procesów.swapVisibleLimit miejsca wymiany (
memory.swap.max) w MiB stosowany do widocznych procesów.swapNotVisibleLimit miejsca wymiany (
memory.swap.max) w MiB stosowany do niewidocznych procesów.
Standardowe limity platformy
W Androidzie 17 i nowszych wersjach platforma określa standardowe limity pamięci i miejsca wymiany
w /system/etc/memory-limiter-config.xml w przypadku typowych warstw fizycznej pamięci RAM
za pomocą [floor, ceiling) przedziałów:
| Docelowa fizyczna pamięć RAM |
Zakres MemTotal [dolna granica, górna granica) (minimumRequiredMemTotal, maximumAllowedMemTotal) |
Limit pamięci widocznej (memVisible) |
Limit pamięci niewidocznej (memNotVisible) |
Limit miejsca wymiany widocznego (swapVisible) |
Limit miejsca wymiany niewidocznego (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | [14336, 18432) MiB |
10 240 MiB (10 GiB) | 5120 MiB (5 GiB) | 5120 MiB (5 GiB) | 5120 MiB (5 GiB) |
| 12 GB | [9216, 14336) MiB |
8192 MiB (8 GiB) | 4096 MiB (4 GiB) | 4096 MiB (4 GiB) | 4096 MiB (4 GiB) |
| 8 GB | [6800, 9216) MiB |
5120 MiB (5 GiB) | 3072 MiB (3 GiB) | 3072 MiB (3 GiB) | 3072 MiB (3 GiB) |
| 6 GB | [4800, 6800) MiB |
4096 MiB (4 GiB) | 2048 MiB (2 GiB) | 2048 MiB (2 GiB) | 2048 MiB (2 GiB) |
| 4 GB | [3200, 4800) MiB |
2048 MiB (2 GiB) | 1024 MiB (1 GiB) | 1024 MiB (1 GiB) | 1024 MiB (1 GiB) |
Zasady dotyczące limitów pamięci
Konfiguracja ogranicznika pamięci jest oparta na tych zasadach platformy:
Spójność ekosystemu i zgodność aplikacji: spójne limity pamięci na różnych urządzeniach zapewniają przewidywalną wydajność aplikacji w całym ekosystemie Androida. Aplikacje są tworzone i testowane pod kątem standardowych oczekiwań dotyczących pamięci. Utrzymanie jednolitych limitów platformy pozwala uniknąć nieoczekiwanego ograniczania lub przedwczesnego zamykania.
Proporcjonalne przydzielanie zasobów: limity platformy są kalibrowane zgodnie z pojemnością fizycznej pamięci RAM:
- Widoczne procesy: przydzielono im około 1/2 do 2/3 łącznej fizycznej pamięci RAM, aby obsługiwać aktywne interfejsy, kompozycje i obciążenia związane z renderowaniem.
- Niewidoczne procesy: przydzielono im około 1/4 do 1/3 łącznej fizycznej pamięci RAM na potrzeby zadań w tle.
Uniwersalne zastosowanie: limity obowiązują jednolicie wszystkie procesy aplikacji na urządzeniu (UID >= 10000), w tym preinstalowane aplikacje systemowe i aplikacje OEM. Ogranicznik pamięci nie obsługuje list dozwolonych, aby wykluczyć określone aplikacje, co zapewnia sprawiedliwe zarządzanie pamięcią we wszystkich aplikacjach.
Brak interfejsów API zapytań w czasie działania: w Androidzie 17 i nowszych wersjach aplikacje nie mogą programowo wysyłać zapytań o przypisane limity pamięci w czasie działania. Standardowe limity platformy są zdefiniowane z dużym marginesem, dzięki czemu dobrze działające aplikacje działają bez ograniczeń podczas normalnego użytkowania.
Odzyskiwanie pamięci przez jądro i dynamika miejsca wymiany: gdy proces aplikacji zbliża się do limitu
memory.high, jądro systemu Linux kieruje odzyskiwanie pamięci konkretnie do cgroup tego procesu. Obejmuje to usuwanie nieaktywnych stron z plikami i przenoszenie nieużywanych stron anonimowych do ZRAM, co zapewnia płynność systemu bez obciążania globalnej pamięci.
Polecenia powłoki
Polecenie am memory-limiter umożliwia deweloperom i integratorom systemów interakcję z usługą w czasie działania na potrzeby tworzenia, testowania i debugowania:
am memory-limiter <SUB-COMMAND>status
Podpolecenie status zgłasza stan operacyjny i aktywne dane ogranicznika pamięci:
adb shell am memory-limiter statusOto przykładowe dane wyjściowe:
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
Kluczowe pola w danych wyjściowych:
monitoring- Wskazuje, czy ogranicznik pamięci aktywnie obserwuje procesy.
visibleMeminotVisibleMem- Obliczone limity bezwzględne pamięci (
memory.high) obecnie egzekwowane dla każdego stanu widoczności. visibleSwapinotVisibleSwap- Obliczone limity bezwzględne miejsca wymiany (
memory.swap.max) obecnie egzekwowane dla każdego stanu widoczności. events- Liczba przypadków, w których proces przekroczył przypisany limit.
processes- Aktualna liczba monitorowanych procesów.
ignoruj
Podpolecenie ignore tymczasowo wyklucza z ograniczania pamięci określony UID lub wszystkie procesy. Jest to przydatne do testowania wydajności, testowania obciążenia lub diagnozowania zachowania pamięci:
# Ignore a specific UID adb shell am memory-limiter ignore 10087# Ignore all processes (temporarily disables limiting) adb shell am memory-limiter ignore all# Resume normal limiting operation adb shell am memory-limiter ignore none
ręcznie
Podpolecenie manual zastępuje obliczone limity dla określonego procesu według identyfikatora PID niestandardową wartością bezwzględną w bajtach. Wartość musi być liczbą całkowitą, ale może zawierać sufiks MB, aby wskazać MiB, lub sufiks GB, aby wskazać GiB:
# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1073741824# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1024MB# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1GB# Remove the manual override for PID 1234 adb shell am memory-limiter manual 1234 none
Ręczne zastąpienia obowiązują tylko przez cały okres istnienia konkretnej instancji procesu. Jeśli proces zostanie ponownie uruchomiony, powróci do standardowych limitów na podstawie swojego stanu.
Ręczne zastąpienie nie może przekraczać fizycznych limitów systemu.