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 procesów działających na pierwszym planie i procesów w pamięci podręcznej z powodu braku pamięci.
Mechanizm
Ogranicznik pamięci integruje się z usługą Activity Manager (AMS), aby śledzić zdarzenia związane z cyklem ż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 korzysta z tych atrybutów:
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 jej nieaktywną pamięć anonimową, 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:
Procesy widoczne: procesy hostujące interfejs widoczny dla użytkownika (np. obecnie aktywna aktywność lub aktywna interakcja). Ponieważ wyświetlanie interfejsu wymaga większego zestawu roboczego do renderowania i potoków graficznych, procesy widoczne otrzymują większy limit pamięci.
Procesy niewidoczne: procesy działające w tle bez wyświetlania interfejsu (np. usługi działające w tle lub odbiorniki transmisji). Ponieważ nie renderują 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ą zamrażane, a następnie maksymalnie odzyskiwane.
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 udostępnia standardowy plik konfiguracyjny na partycji systemowej, a jednocześnie obsługuje opcjonalne zastąpienia specyficzne dla urządzenia na partycji vendor:
- Domyślna konfiguracja systemu:
/system/etc/memory-limiter-config.xml - Zastąpienie dostawcy (opcjonalne):
/vendor/etc/memory-limiter-config.xml
Ogranicznik pamięci najpierw sprawdza plik /vendor/etc/memory-limiter-config.xml.
Jeśli jest obecny, używa konfiguracji dostawcy. W przeciwnym razie wraca do domyślnej konfiguracji systemu w pliku /system/etc/memory-limiter-config.xml.
Format XML
Plik konfiguracyjny 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 minimums account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
<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
MemTotal(w MiB) wymagana do zastosowania tego zestawu limitów. Usługa porównuje tę wartość z łączną ilością dostępnej pamięci zgłoszoną przez jądro w pliku/proc/meminfo.Informacje o wyłączeniach 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 1 GiB pamięci fizycznej.Ponieważ
MemTotaljest niższa niż reklamowana 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 wartośćminimumRequiredMemTotaljako13312(13 GiB), a nie16384(16 GiB), a poziom dla urządzeń z 12 GB pamięci określa wartość10240(10 GiB). Dzięki temu każde urządzenie pasuje do zamierzonego poziomu niezależnie od różnic w wyłączeniach na poziomie płyty.memVisibleLimit pamięci (
memory.high) w MiB stosowany do procesów widocznych.memNotVisibleLimit pamięci (
memory.high) w MiB stosowany do procesów niewidocznych.swapVisibleLimit miejsca wymiany (
memory.swap.max) w MiB stosowany do procesów widocznych.swapNotVisibleLimit miejsca wymiany (
memory.swap.max) w MiB stosowany do procesów niewidocznych.
Standardowe limity platformy
W Androidzie 17 i nowszych wersjach platforma określa standardowe limity pamięci i miejsca wymiany w pliku /system/etc/memory-limiter-config.xml w przypadku typowych poziomów fizycznej pamięci RAM:
| Docelowa fizyczna pamięć RAM |
Minimalna wymagana pamięć MemTotal (minimumRequiredMemTotal) |
Limit pamięci widocznej (memVisible) |
Limit pamięci niewidocznej (memNotVisible) |
Limit miejsca wymiany widocznego (swapVisible) |
Limit miejsca wymiany niewidocznego (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | 13 312 MiB (13 GiB) | 10 240 MiB (10 GiB) | 5120 MiB (5 GiB) | 5120 MiB (5 GiB) | 5120 MiB (5 GiB) |
| 12 GB | 10 240 MiB (10 GiB) | 8192 MiB (8 GiB) | 4096 MiB (4 GiB) | 4096 MiB (4 GiB) | 4096 MiB (4 GiB) |
| 8 GB | 6656 MiB (6,5 GiB) | 5120 MiB (5 GiB) | 3072 MiB (3 GiB) | 3072 MiB (3 GiB) | 3072 MiB (3 GiB) |
| 6 GB | 4608 MiB (4,5 GiB) | 4096 MiB (4 GiB) | 2048 MiB (2 GiB) | 2048 MiB (2 GiB) | 2048 MiB (2 GiB) |
| 4 GB | 3072 MiB (3 GiB) | 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:
- Procesy widoczne: 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.
- Procesy niewidoczne: 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, które wykluczają 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 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 pamięci globalnej.
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 raportuje stan operacyjny i aktywne dane ogranicznika pamięci:
adb shell am memory-limiter statusPrzykł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 bezwzględne limity pamięci (
memory.high) obecnie egzekwowane dla każdego stanu widoczności. visibleSwapinotVisibleSwap- Obliczone bezwzględne limity 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 ograniczenia 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 wskazujący MiB lub sufiks GB wskazujący 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 czas życia konkretnej instancji procesu. Jeśli proces zostanie ponownie uruchomiony, wróci do standardowych limitów na podstawie swojego stanu.
Ręczne zastąpienie nie może przekraczać fizycznych limitów systemu.