Ogranicznik pamięci

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ą grupy kontrolnej Linux cgroup v2. 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 krytycznych procesów 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), z której może korzystać 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 usuwa jej czystą pamięć opartą na plikach i zamienia jej nieaktywną pamięć anonimową, aby utrzymać aplikację w granicach limitu. W wyniku tego wykluczenia strony i operacji 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 przestrzeni wymiany, aplikacja nie może przydzielić pamięci i zostaje zamknięta.

Monitorowanie procesów

Ogranicznik pamięci stosuje limity platformy podane na tej stronie do procesów aplikacji (UID >= 10000). Procesy z identyfikatorem UID poniżej 10000 nie podlegają limitom platformy. Nie dotyczy to też pakietu domyślnej usługi wnioskowania w piaskownicy na urządzeniu (config_defaultOnDeviceSandboxedInferenceService). Aplikacje i usługi systemowe mogą nadal deklarować mniejsze budżety pamięci, jak opisano w sekcji Budżety pamięci systemowej.

Ogranicznik pamięci przypisuje limity pamięci na podstawie stanu widoczności procesu:

  • Widoczne procesy: 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 do 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 tabeli poniżej znajdziesz mapowanie konkretnych stanów procesów na klasyfikacje limitów pamięci platformy:

Stan procesuLimit pamięci
PERSISTENTBez ograniczeń
PERSISTENT_UIBez ograniczeń
TOPWidoczne
BOUND_TOPWidoczne
FOREGROUND_SERVICENiewidoczne
BOUND_FOREGROUND_SERVICENiewidoczne
IMPORTANT_FOREGROUNDWidoczne
IMPORTANT_BACKGROUNDNiewidoczne
TRANSIENT_BACKGROUNDNiewidoczne
BACKUPNiewidoczne
SERVICENiewidoczne
RECEIVERNiewidoczne
TOP_SLEEPINGWidoczne
HEAVY_WEIGHTNiewidoczne
HOMENiewidoczne
LAST_ACTIVITYNiewidoczne
CACHED_ACTIVITYW pamięci podręcznej
CACHED_ACTIVITY_CLIENTW pamięci podręcznej
CACHED_RECENTW pamięci podręcznej
CACHED_EMPTYW pamięci podręcznej

Procesy PERSISTENT i PERSISTENT_UI nie otrzymują limitu platformy. Jeśli jeden z tych procesów deklaruje budżet pamięci, stosowany jest jego budżet na pierwszym planie, zgodnie z opisem w sekcji Zasobniki budżetu pamięci.

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.

Zasobniki budżetu pamięci

Na urządzeniach, które obsługują budżety pamięci, aplikacje i usługi systemowe mogą deklarować budżety, które są bardziej rygorystyczne niż limity platformy. Aplikacje deklarują budżety w swoim pliku manifestu, jak opisano w sekcji Ustawianie budżetów pamięci aplikacji. Informacje o aplikacjach i usługach systemowych znajdziesz w artykule Budżety pamięci systemowej. Budżet określa memory.high i nie może przekraczać limitu platformy dla tego samego stanu procesu.

Gdy proces ma budżet, ogranicznik pamięci mapuje stan procesu na jeden z 4 zasobników. Wartości pliku manifestu android:state foreground, perceptible i background wybierają budżet dla pasującego zasobnika. Procesy w zasobniku z pamięci podręcznej nie mają aktywnego budżetu.

Stan procesuKategoria budżetu
PERSISTENTPierwszy plan
PERSISTENT_UIPierwszy plan
TOPPierwszy plan
BOUND_TOPPierwszy plan
TOP_SLEEPINGPierwszy plan
FOREGROUND_SERVICEDostrzegalne
BOUND_FOREGROUND_SERVICEDostrzegalne
IMPORTANT_FOREGROUNDDostrzegalne
IMPORTANT_BACKGROUNDDostrzegalne
TRANSIENT_BACKGROUNDTło
BACKUPTło
SERVICETło
RECEIVERTło
HEAVY_WEIGHTTło
HOMETło
LAST_ACTIVITYTło
CACHED_ACTIVITYW pamięci podręcznej
CACHED_ACTIVITY_CLIENTW pamięci podręcznej
CACHED_RECENTW pamięci podręcznej
CACHED_EMPTYW pamięci podręcznej

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

Minimalna 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ść jądra MemTotal 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.

Wartość MemTotal jest niższa niż deklarowana fizyczna pamięć DRAM urządzenia, dlatego progi minimumRequiredMemTotal uwzglę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 poziomie 14336 (14 GiB), a nie 16384 (16 GiB), a poziom dla urządzeń z 12 GB pamięci określa dolny próg na poziomie 9216 (9 GiB). Dzięki temu każde urządzenie będzie odpowiadać zamierzonemu poziomowi, niezależnie od różnic w wyłączeniach na poziomie płyty.

maximumAllowedMemTotal

Maksymalna wartość MemTotal systemu (w MiB, wyłączny pułap), dla której ten limit ma zastosowanie. Jeśli wartość MemTotal jest większa od tej wartości maksymalnej lub jej 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ń.

memVisible

Miękki limit pamięci (memory.high) w MiB stosowany do widocznych procesów.

memNotVisible

Miękki limit pamięci (memory.high) w MiB stosowany do niewidocznych procesów.

swapVisible

Limit przestrzeni wymiany (memory.swap.max) w MiB stosowany do widocznych procesów.

swapNotVisible

Limit 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 wymiany niewidocznych elementów
(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 przydzielono około 1/4 do 1/3 łącznej ilości fizycznej 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. Oprócz domyślnej usługi wnioskowania w piaskownicy na urządzeniu ogranicznik pamięci nie wyklucza poszczególnych aplikacji, co zapewnia sprawiedliwe zarządzanie pamięcią we wszystkich aplikacjach. Aby skonfigurować samoczynnie dostosowujące się dolne limity ochrony memory.min w przypadku krytycznych interaktywnych demonów oraz bardziej rygorystyczne, dostosowane do obciążenia górne limity memory.high w 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żymi marginesami, dzięki czemu dobrze działające aplikacje działają bez ograniczeń podczas normalnego użytkowania.

  • Dynamika odzyskiwania i wymiany pamięci jądra: 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 status

Oto 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 danych wyjściowych:

monitoring
Wskazuje, czy ogranicznik pamięci aktywnie monitoruje procesy.
visibleMem i notVisibleMem
Obliczone bezwzględne limity pamięci (memory.high) obecnie stosowane w przypadku każdego stanu widoczności.
visibleSwap i notVisibleSwap
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 trwania danej instancji procesu. Jeśli proces zostanie uruchomiony ponownie, limity zostaną przywrócone do standardowych wartości na podstawie stanu.

Ręczne zastąpienie nie może przekraczać fizycznych limitów systemu.