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 zapobiega zużywaniu przez poszczególne aplikacje nieproporcjonalnie dużej ilości pamięci systemowej, co zmniejsza ogólne obciążenie pamięci i zapobiega agresywnemu zamykaniu procesów krytycznych 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 Service (AMS), aby śledzić zdarzenia cyklu życia procesu i zmiany stanu. Ogranicznik pamięci wymusza limity pamięci przy użyciu hierarchii cgroup v2 jądra systemu Linux.
Aby obsługiwać ogranicznik pamięci, jądro urządzenia musi mieć włączoną grupę kontrolną w wersji 2 i kontroler memory. Usługa korzysta z tych atrybutów:
memory.high- Limit. Po przekroczeniu tego limitu proces jest ograniczany, a jądro próbuje proaktywnie odzyskać stronę z tego procesu.
memory.swap.max- Ogranicza maksymalną ilość przestrzeni wymiany (np. ZRAM), którą może wykorzystać proces.
Wpływ na aplikacje
Aplikacje, które działają w ramach limitów pamięci, nie są objęte działaniem ogranicznika pamięci.
Gdy aplikacja przekroczy limit memory.high, jądro usuwa jej czystą pamięć opartą na plikach i zamienia nieaktywną pamięć anonimową, aby utrzymać aplikację w granicach limitu. W wyniku tego wykluczenia strony i wymiany aplikacja może doświadczyć tymczasowego ograniczenia wykonywania, ale nadal będzie działać.
W skrajnych sytuacjach, takich jak niekontrolowany wyciek pamięci, gdy aplikacja nadal przydziela pamięć anonimową po wyczerpaniu pamięci 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 systemu podstawowego są wyłączone, aby zachować niezbędną 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. aktywność, na której użytkownik jest obecnie skupiony, lub aktywną interakcję). Wyświetlanie interfejsu wymaga większego zestawu roboczego na potrzeby renderowania i potoków graficznych, dlatego widoczne procesy mają większy limit pamięci.
Niewidoczne procesy: procesy wykonujące pracę w tle bez wyświetlania interfejsu (np. usługi w tle lub odbiorniki komunikatów). Ponieważ nie renderują komponentów interfejsu, obowiązuje je bardziej restrykcyjny limit.
W tej tabeli znajdziesz mapowanie konkretnych stanów procesów 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 zapisanym 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 zarejestrowanie anomalii w statsd.
Konfiguracja
W Androidzie 17 i nowszych ogranicznik pamięci jest konfigurowany za pomocą standardowego pliku konfiguracji platformy na partycji systemowej w /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.Zrozumienie wydzielonych obszarów i granic pamięci: wartość
MemTotaljądra odzwierciedla fizyczną pamięć DRAM dostępną dla systemu operacyjnego po odjęciu wydzielonych obszarów pamięci sprzętu i oprogramowania układowego. Wydzielone obszary rezerwują pamięć RAM dla specjalistycznych komponentów sprzętowych, takich jak GPU, modem pasma podstawowego, procesor sygnałowy aparatu, bezpieczne środowiska wykonawcze i hipernadzorcy, które zwykle zużywają od 500 MiB do ponad 2 GiB pamięci fizycznej.Ponieważ wartość
MemTotaljest niższa niż deklarowana fizyczna pamięć DRAM urządzenia, progiminimumRequiredMemTotaluwzględniają margines na te wyłączenia. Na przykład konfiguracja kierowana na urządzenia z 16 GB pamięci określa dolny próg na poziomie14336(14 GiB), a nie16384(16 GiB), a poziom dla urządzeń z 12 GB pamięci określa dolny próg na poziomie9216(9 GiB). Dzięki temu każde urządzenie będzie odpowiadać zamierzonemu poziomowi niezależnie od różnic w wycięciach na poziomie płyty.maximumAllowedMemTotalMaksymalna ilość pamięci systemowej
MemTotal(w MiB, bez górnego limitu), dla której ten limit ma zastosowanie. Jeśli wartośćMemTotaljest większa niż ten pułap lub mu równa, ustawiony limit nie ma zastosowania. W przypadku urządzeń z pamięcią 18 432 MiB lub większą albo mniejszą niż 3200 MiB ogranicznik pamięci nie stosuje limitów, pozostawiając procesy bez ograniczeń.memVisibleMiękki limit pamięci (
memory.high) w MiB stosowany do widocznych procesów.memNotVisibleMiękki limit pamięci (
memory.high) w MiB stosowany do niewidocznych procesów.swapVisibleLimit przestrzeni wymiany (
memory.swap.max) w MiB stosowany do widocznych procesów.swapNotVisibleLimit przestrzeni wymiany (
memory.swap.max) w MiB stosowany do niewidocznych procesów.
Standardowe limity platformy
W Androidzie 17 i nowszych platforma określa standardowe limity pamięci i pamięci wymiany w /system/etc/memory-limiter-config.xml na różnych poziomach fizycznej pamięci RAM za pomocą przedziałów zakresu [floor, ceiling):
| Docelowa fizyczna pamięć RAM |
Zakres MemTotal [dolny, górny) (minimumRequiredMemTotal, maximumAllowedMemTotal) |
Limit widocznej pamięci (memVisible) |
Limit pamięci niewidocznej (memNotVisible) |
Limit widocznych zamian (swapVisible) |
Limit zamian niewidocznych (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 limitu pamięci
Konfiguracja ogranicznika pamięci została zaprojektowana zgodnie z tymi zasadami 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 przepustowości lub przedwczesnego zakończenia działania.
Proporcjonalne przydzielanie zasobów: limity platformy są kalibrowane zgodnie z pojemnością fizycznej pamięci RAM:
- Widoczne procesy: przydzielono około 1/2 do 2/3 całkowitej fizycznej pamięci RAM na potrzeby obsługi aktywnego interfejsu, kompozycji i renderowania.
- Niewidoczne procesy: na zadania w tle przydzielane jest około 1/4 do 1/3 całkowitej pamięci RAM.
Uniwersalne zastosowanie: limity obowiązują jednolicie we wszystkich procesach aplikacji na urządzeniu (UID >= 10000), w tym we wstępnie zainstalowanych aplikacjach systemowych i OEM. Ogranicznik pamięci nie obsługuje list dozwolonych, które umożliwiają wykluczenie określonych aplikacji, co zapewnia sprawiedliwe zarządzanie pamięcią we wszystkich aplikacjach. Aby skonfigurować samoczynnie dostosowujące się progi ochrony
memory.minw przypadku krytycznych interaktywnych demonów oraz bardziej rygorystyczne, dostosowane do obciążenia limitymemory.highw przypadku poszczególnych aplikacji i usług systemowych, zapoznaj się z artykułem Budżety pamięci systemowej.Brak interfejsów API do zapytań w czasie działania: na Androidzie 17 i nowszych aplikacje nie mogą automatycznie wysyłać zapytań o przypisane limity pamięci w czasie działania. Standardowe limity platformy są definiowane z dużym marginesem, dzięki czemu dobrze działające aplikacje mogą działać bez ograniczeń podczas normalnego użytkowania.
Dynamika odzyskiwania i wymiany pamięci przez jądro: gdy proces aplikacji zbliża się do limitu
memory.high, jądro Linuksa kieruje odzyskiwanie pamięci konkretnie do grupy kontrolnej tego procesu. Obejmuje to usuwanie nieaktywnych stron opartych na plikach i przenoszenie nieużywanych stron anonimowych do ZRAM, co pozwala zachować płynność działania 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 w celu programowania, testowania i debugowania:
am memory-limiter <SUB-COMMAND>status
Podpolecenie status raportuje stan działania i aktywne wskaźniki 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
Najważniejsze pola w wynikach:
monitoring- Wskazuje, czy ogranicznik pamięci aktywnie monitoruje procesy.
visibleMeminotVisibleMem- Obliczone bezwzględne limity pamięci (
memory.high) obecnie stosowane w przypadku każdego stanu widoczności. visibleSwapinotVisibleSwap- Obliczone bezwzględne limity zamiany (
memory.swap.max) obecnie stosowane w przypadku każdego stanu widoczności. events- Liczba przypadków, w których proces przekroczył przypisany limit.
processes- Bieżąca liczba monitorowanych procesów.
ignoruj
Podpolecenie ignore tymczasowo wyklucza określony identyfikator UID lub wszystkie procesy z ograniczania pamięci. Jest to przydatne do testów porównawczych wydajności, testów 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 konkretnego procesu według identyfikatora procesu (PID) niestandardową wartością bezwzględną w bajtach. Wartość musi być liczbą całkowitą, ale może zawierać sufiks MB oznaczający MiB lub sufiks GB oznaczają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 działania danej instancji procesu. Jeśli proces zostanie uruchomiony ponownie, powróci do standardowych limitów na podstawie swojego stanu.
Ręczne zastąpienie nie może przekraczać fizycznych limitów systemu.