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 między ważne procesy systemowe i wiele aplikacji użytkownika.

Na tej stronie omówimy podstawy przydzielania pamięci przez Androida na potrzeby systemu i aplikacji użytkownika. Wyjaśnimy też, jak system operacyjny reaguje na sytuacje, w których jest mało 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 danych.

Strony pamięci

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

Strony są uważane za wolne lub używane. Wolne strony to nieużywana pamięć RAM. Używane strony to pamięć RAM, z której aktywnie korzysta 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ży do 1 procesu i nie jest udostę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.
    • Udostępniona: 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 w zarządzaniu sytuacjami, w których jest mało pamięci. W następnej sekcji tego dokumentu wyjaśnimy je bardziej szczegółowo.

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

Android ma 2 główne mechanizmy radzenia sobie z sytuacjami, w których jest mało pamięci: demona wymiany jądra i mechanizm zabijania procesów przy małej ilości pamięci.

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 uzyskać dostęp 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ęć RAM (wolne strony). Jeśli proces spróbuje uzyskać dostęp do 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 kończyć procesy.

Zmodyfikowana strona przeniesiona do pamięci zRAM i skompresowana

Rysunek 3. Zmodyfikowana strona przeniesiona do zRAM i skompresowana

Mechanizm zabijania procesów przy małej ilości pamięci

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 zaczyna brakować pamięci i że powinna zmniejszyć przydział. Jeśli to nie wystarczy, jądro zacznie kończyć procesy, aby zwolnić pamięć. W tym celu używa mechanizmu zabijania procesów przy małej ilości pamięci (LMK).

Aby zdecydować, który proces zakończyć, LMK używa wyniku "brak pamięci" o nazwie oom_adj_score aby określić priorytet uruchomionych procesów. Najpierw kończone są procesy z wysokim wynikiem. Najpierw kończone są aplikacje działające w tle, a na końcu procesy systemowe. W tabeli poniżej wymieniliśmy kategorie wyników LMK od najwyższego do najniższego. Najpierw zostaną zakończone elementy z kategorii o najwyższym wyniku w pierwszym wierszu:

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 zakończy 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 aplikacja uruchamiająca. Jej zakończenie 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. Zakończenie 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 zakończeniu tych procesów telefon może się zrestartować.

  • Natywne: procesy o bardzo niskim poziomie 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 udostępnione. Aplikacje, które mają dostęp do tej samej usługi lub biblioteki, będą udostępniać strony pamięci. Na przykład Usługi Google Play i aplikacja do gier mogą udostępniać 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 udostępniane przez 2 aplikacje (środek)

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

  • Rozmiar zestawu rezydentnego (RSS): liczba stron udostępnionych i nieudostępnionych używanych przez aplikację.
  • Proporcjonalny rozmiar zestawu (PSS): liczba stron nieudostępnionych używanych przez aplikację i równomierny rozkład stron udostępnionych (np. jeśli 3 procesy udostępniają 3 MB, każdy proces otrzymuje 1 MB w PSS).
  • Rozmiar unikalnego zestawu (USS): liczba stron nieudostępnionych używanych przez aplikację (strony udostępnione 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ą udostępniane i przez ile procesów. RSS nie rozróżnia stron udostępnionych i nieudostępnionych (co przyspiesza obliczenia) i lepiej nadaje się do śledzenia zmian w alokacji pamięci.

Dodatkowe materiały