Przydział pamięci między procesy

Platforma Android działa na założeniu, że wolna pamięć to pamięć zmarnowana. Stara się ona zawsze wykorzystywać całą dostępną pamięć. Na przykład system przechowuje aplikacje w pamięci po ich zamknięciu, aby użytkownik mógł szybko do nich wrócić. Z tego powodu urządzenia z Androidem często działają z bardzo małą ilością wolnej pamięci. Zarządzanie pamięcią jest niezbędne do prawidłowego przydzielania pamięci ważnym procesom systemowym i wielu aplikacjom użytkownika.

Na tej stronie znajdziesz podstawowe informacje o tym, jak Android przydziela pamięć systemowi i aplikacjom użytkownika. Wyjaśniamy też, jak system operacyjny reaguje na sytuacje, w których brakuje pamięci.

Rodzaje pamięci

Urządzenia z Androidem mają 3 rodzaje pamięci: RAM, zRAM i pamięć na dane. Pamiętaj, że zarówno procesor, jak i GPU mają dostęp do tej samej pamięci RAM.

Rodzaje pamięci

Rysunek 1. Rodzaje pamięci – RAM, zRAM i pamięć na dane

  • RAM to najszybszy rodzaj pamięci, ale zwykle ma ograniczoną pojemność. Najwięcej pamięci RAM mają urządzenia z wyższej półki.

  • zRAM to partycja pamięci RAM używana jako przestrzeń wymiany. Wszystko jest kompresowane, gdy jest umieszczane w zRAM, a następnie dekompresowane, gdy jest z niej kopiowane. Ta część pamięci RAM zwiększa lub zmniejsza swój rozmiar w miarę przenoszenia stron do zRAM lub z niej. Producenci urządzeń mogą ustawić maksymalny rozmiar.

  • Pamięć na dane zawiera wszystkie trwałe dane, takie jak system plików i dołączony kod obiektowy wszystkich aplikacji, bibliotek i platformy. Pamięć na dane ma znacznie większą pojemność niż pozostałe 2 rodzaje pamięci. W Androidzie pamięć na dane nie jest używana jako przestrzeń wymiany, jak w innych implementacjach systemu Linux, ponieważ częste zapisywanie może powodować zużycie tej pamięci i skrócić żywotność nośnika.

Strony pamięci

Pamięć RAM jest podzielona na strony. Zwykle każda strona ma 4 KB pamięci.

Strony są uznawane za wolne lub używane. Wolne strony to nieużywana pamięć RAM. Używane strony to pamięć RAM, która jest aktywnie używana przez system. Są one podzielone na te kategorie:

  • Pamięć podręczna: pamięć obsługiwana przez plik w pamięci na dane (np. kod lub pliki mapowane w pamięci). Istnieją 2 rodzaje pamięci podręcznej:
    • Prywatna: należąca do jednego procesu i nieudostępniana.
      • Czysta: niezmodyfikowana kopia pliku w pamięci na dane, którą można usunąć za pomocą kswapd, aby zwiększyć ilość wolnej pamięci.
      • Zanieczyszczona: zmodyfikowana kopia pliku w pamięci na dane; można ją przenieść do zRAM lub skompresować w zRAM za pomocą kswapd, aby zwiększyć ilość wolnej pamięci.
    • Współdzielona: używana przez wiele procesów.
      • Czysta: niezmodyfikowana kopia pliku w pamięci na dane, którą można usunąć za pomocą kswapd, aby zwiększyć ilość wolnej pamięci.
      • Zanieczyszczona: zmodyfikowana kopia pliku w pamięci na dane; umożliwia zapisywanie zmian z powrotem do pliku w pamięci na dane, aby zwiększyć ilość wolnej pamięci za pomocą lub jawnie za pomocą kswapd msync() lub munmap()
  • Anonimowa: pamięć nie obsługiwana przez plik w pamięci na dane (np. przydzielona przez mmap() z ustawioną flagą MAP_ANONYMOUS)
    • Zmodyfikowana: można ją przenieść do zRAM lub skompresować w zRAM za pomocą kswapd, aby zwiększyć ilość wolnej pamięci.

Proporcje wolnych i używanych stron zmieniają się w czasie, ponieważ system aktywnie zarządza pamięcią RAM. Koncepcje przedstawione w tej sekcji są kluczowe do radzenia sobie z sytuacjami, w których brakuje pamięci. Więcej informacji na ten temat znajdziesz w następnej sekcji tego dokumentu.

Zarządzanie małą ilością pamięci

Android ma 2 główne mechanizmy radzenia sobie z sytuacjami, w których brakuje pamięci: demona wymiany jądra i mechanizm low-memory killer.

Demon wymiany jądra

Demon wymiany jądra (kswapd) jest częścią jądra systemu Linux i przekształca używaną pamięć w wolną. Demon staje się aktywny, gdy na urządzeniu zaczyna brakować wolnej pamięci. Jądro systemu Linux utrzymuje niskie i wysokie progi wolnej pamięci. Gdy ilość wolnej pamięci spadnie poniżej niskiego progu, kswapd zaczyna odzyskiwać pamięć. Gdy ilość wolnej pamięci osiągnie wysoki próg, kswapd przestaje odzyskiwać pamięć.

kswapd może odzyskać czyste strony, usuwając je, ponieważ są one obsługiwane przez pamięć na dane i nie zostały zmodyfikowane. Jeśli proces spróbuje odwołać się do usuniętej czystej strony, system skopiuje ją z pamięci na dane do pamięci RAM. Ta operacja jest nazywana stronicowaniem na żądanie.

Usunięto czystą stronę z kopią zapasową w pamięci

Rysunek 2. Czysta strona obsługiwana przez pamięć na dane została usunięta

kswapd może przenosić zmodyfikowane strony prywatne z pamięci podręcznej i zmodyfikowane strony anonimowe do zRAM, gdzie są kompresowane. Dzięki temu zwalnia się dostępna pamięć w RAM (wolne strony). Jeśli proces spróbuje dotknąć zmodyfikowanej strony w zRAM, strona zostanie zdekompresowana i przeniesiona z powrotem do pamięci RAM. Jeśli proces powiązany ze skompresowaną stroną zostanie zakończony, strona zostanie usunięta z zRAM.

Jeśli ilość wolnej pamięci spadnie poniżej określonego progu, system zacznie zamykać procesy.

Zmodyfikowana strona przeniesiona do zRAM i skompresowana

Rysunek 3. Zmodyfikowana strona została przeniesiona do zRAM i skompresowana

Mechanizm low-memory killer

W wielu przypadkach kswapd nie może zwolnić wystarczającej ilości pamięci dla systemu. W takim przypadku system używa onTrimMemory() , aby powiadomić aplikację, że brakuje pamięci i że powinna zmniejszyć przydział. Jeśli to nie wystarczy, jądro zacznie zamykać procesy, aby zwolnić pamięć. W tym celu używa mechanizmu low-memory killer (LMK).

Aby zdecydować, który proces zamknąć, LMK używa wyniku "brak pamięci (OOM)" o nazwie oom_adj_score aby określić priorytet uruchomionych procesów. Najpierw zamykane są procesy z wysokim wynikiem. Najpierw zamykane są aplikacje działające w tle, a na końcu procesy systemowe. W tabeli poniżej znajdziesz kategorie wyników LMK od najwyższego do najniższego. Najpierw zostaną zamknięte elementy z kategorii o najwyższym wyniku, czyli z pierwszego wiersza:

Procesy Androida, najwyższe wyniki u góry

Rysunek 4. Procesy Androida z wysokimi wynikami u góry i niskimi u dołu

Poniżej znajdziesz opisy poszczególnych kategorii w tabeli powyżej:

  • Aplikacje działające w tle: aplikacje, które były wcześniej uruchomione i nie są obecnie aktywne. LMK najpierw zamknie aplikacje działające w tle, zaczynając od tej z najwyższym wynikiem oom_adj_score.

  • Poprzednia aplikacja: ostatnio używana aplikacja działająca w tle. Poprzednia aplikacja ma wyższy priorytet (niższy wynik) niż aplikacje działające w tle, ponieważ jest bardziej prawdopodobne, że użytkownik przełączy się na nią niż na jedną z aplikacji działających w tle.

  • Aplikacja Home: jest to program uruchamiający. Jej zamknięcie spowoduje zniknięcie tapety.

  • Usługi: usługi są uruchamiane przez aplikacje i mogą obejmować synchronizację lub przesyłanie do chmury.

  • Aplikacje widoczne: aplikacje, które nie działają na pierwszym planie, ale są w jakiś sposób widoczne dla użytkownika, np. uruchamiają proces wyszukiwania, który wyświetla mały interfejs, lub odtwarzają muzykę.

  • Aplikacja działająca na pierwszym planie: aplikacja, która jest obecnie używana. Zamknięcie aplikacji działającej na pierwszym planie wygląda jak awaria aplikacji, co może sugerować użytkownikowi, że z urządzeniem coś jest nie tak.

  • Trwałe (usługi): są to podstawowe usługi urządzenia, takie jak telefonia i Wi-Fi.

  • System: procesy systemowe. Po zamknięciu tych procesów telefon może się zrestartować.

  • Natywne: procesy niskiego poziomu używane przez system (np. kswapd).

Producenci urządzeń mogą zmienić działanie LMK.

Obliczanie wykorzystania pamięci

Jądro śledzi wszystkie strony pamięci w systemie.

Strony używane przez różne procesy

Rysunek 5. Strony używane przez różne procesy

Określając, ile pamięci jest używane przez aplikację, system musi uwzględnić strony współdzielone. Aplikacje, które mają dostęp do tej samej usługi lub biblioteki, będą współdzielić strony pamięci. Na przykład Usługi Google Play i aplikacja do gier mogą współdzielić usługę lokalizacji. Utrudnia to określenie, ile pamięci należy do usługi jako całości, a ile do każdej aplikacji.

Strony udostępnione przez 2 aplikacje

Rysunek 6. Strony współdzielone przez 2 aplikacje (środek)

Aby określić wykorzystanie pamięci przez aplikację, można użyć dowolnego z tych wskaźników:

  • Resident Set Size (RSS): liczba stron współdzielonych i niewspółdzielonych używanych przez aplikację.
  • Proportional Set Size (PSS): liczba stron niewspółdzielonych używanych przez aplikację i równomierny rozkład stron współdzielonych (np. jeśli 3 procesy współdzielą 3 MB, każdy proces otrzymuje 1 MB w PSS).
  • Unique Set Size (USS): liczba stron niewspółdzielonych używanych przez aplikację (strony współdzielone nie są uwzględniane).

PSS jest przydatny dla systemu operacyjnego, gdy chce on wiedzieć, ile pamięci jest używane przez wszystkie procesy, ponieważ strony nie są liczone wielokrotnie. Obliczenie PSS zajmuje dużo czasu, ponieważ system musi określić, które strony są współdzielone i przez ile procesów. RSS nie rozróżnia stron współdzielonych i niewspółdzielonych (co przyspiesza obliczenia) i lepiej nadaje się do śledzenia zmian w alokacji pamięci.

Diagnozowanie najważniejszych ścieżek użytkownika (CUJ) z dużym wykorzystaniem pamięci

Podczas badania problemów z pamięcią warto zidentyfikować najważniejsze ścieżki użytkownika (CUJ) w aplikacji, które powodują duże wykorzystanie pamięci. Są to konkretne przepływy pracy, które z natury wymagają znacznej alokacji pamięci. Typowe najważniejsze ścieżki użytkownika (CUJ) z dużym wykorzystaniem pamięci to:

  • wczytywanie i manipulowanie złożonymi multimediami lub dużymi bitmapami;
  • hostowanie instancji WebView.

Jeśli Twoja aplikacja korzysta z treści internetowych, instancje WebView mogą często stać się ukrytym wąskim gardłem pamięci ze względu na architekturę wieloprocesową i wykonywanie kodu natywnego. Szczegółowe informacje o diagnostyce, sprawdzonych metodach dotyczących cyklu życia i narzędziach dostosowanych do treści internetowych znajdziesz w artykule Zarządzanie pamięcią WebView i jej diagnozowanie.

Dodatkowe materiały