Przydział pamięci między procesy

Platforma Android działa na zasadzie, że wolna pamięć to zmarnowana pamięć. Zawsze stara się wykorzystać całą dostępną pamięć. Na przykład system zachowuje 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żnymi procesami systemowymi a wieloma aplikacjami użytkownika.

Na tej stronie omawiamy podstawy przydzielania pamięci przez Androida systemowi i aplikacjom użytkownika. Wyjaśnia ona też, jak system operacyjny reaguje na sytuacje związane z niedostateczną ilością pamięci.

Typy pamięci

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

Typy pamięci

Rysunek 1. Rodzaje pamięci: RAM, zRAM i miejsce na dane

  • Pamięć RAM jest najszybszym typem pamięci, ale zwykle ma ograniczony rozmiar. Urządzenia wysokiej klasy mają zwykle najwięcej pamięci RAM.

  • zRAM to partycja pamięci RAM używana na potrzeby przestrzeni wymiany. Wszystko jest kompresowane podczas umieszczania w zRAM, a następnie dekompresowane podczas kopiowania z zRAM. Ten fragment pamięci RAM zwiększa się lub zmniejsza w miarę przenoszenia stron do lub z pamięci zRAM. Maksymalny rozmiar mogą określać producenci urządzeń.

  • Pamięć zawiera wszystkie trwałe dane, takie jak system plików i załączony kod obiektu dla wszystkich aplikacji, bibliotek i platformy. Pamięć masowa ma znacznie większą pojemność niż pozostałe 2 rodzaje pamięci. W Androidzie pamięć nie jest używana do przechowywania danych na dysku, jak to ma miejsce w innych implementacjach systemu Linux, ponieważ częste zapisywanie może powodować zużycie pamięci i skrócenie czasu jej działania.

Strony z pamięcią

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

Strony są uważane za wolne lub używane. Wolne strony to niewykorzystana pamięć RAM. Używane strony to pamięć RAM, z której aktywnie korzysta system. Są one podzielone na te kategorie:

  • Pamięć podręczna: pamięć z wykorzystaniem pliku w pamięci masowej (np. kod lub pliki zmapowane na pamięć). Istnieją 2 rodzaje pamięci podręcznej:
    • Prywatny: należy do jednego procesu i nie jest udostępniony.
      • Czysta: niezmodyfikowana kopia pliku w magazynie, którą można usunąć, aby kswapd zwiększyć ilość wolnej pamięci.
      • Dirty: zmodyfikowana kopia pliku na dysku; można ją przenieść do pamięci RAM lub skompresować w ramach kswapd, aby zwiększyć ilość wolnej pamięci.
    • Udostępnione: używane przez wiele procesów.
      • Clean: niemodyfikowana kopia pliku w pamięci. Można ją usunąć, aby kswapdzwolnić pamięć.
      • Zmodyfikowana: zmodyfikowana kopia pliku w magazynie; umożliwia zapisanie zmian z powrotem do pliku w magazynie, aby zwiększyć ilość wolnej pamięci o kswapd, lub jawne użycie msync()lub munmap()
  • Anonimowy: wspomnienie nie jest oparte na pliku w miejscu na dane (na przykład przydzielone przez mmap()z ustawionym flagą MAP_ANONYMOUS)
    • Dirty: może być przenoszony/skompresowany w zRAM przez kswapd w celu zwiększenia ilości wolnej pamięci.

Proporcje wolnych i zajętych stron zmieniają się w czasie, ponieważ system aktywnie zarządza pamięcią RAM. Pojęcia przedstawione w tej sekcji są kluczowe do zarządzania sytuacjami, w których brakuje pamięci. W następnej sekcji tego dokumentu omawiamy je bardziej szczegółowo.

Zarządzanie pamięcią

Android ma 2 główne mechanizmy radzenia sobie z niedostatkiem pamięci: demon wymiany jądra i system zabijania w przypadku braku pamięci.

demon wymiany jądra

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

kswapd może odzyskać czyste strony, usuwając je, ponieważ są one przechowywane w pamięci masowej i nie zostały zmodyfikowane. Jeśli proces próbuje uzyskać dostęp do czystej strony, która została usunięta, system kopiuje stronę z pamięci masowej do pamięci RAM. Ta operacja jest nazywana przesyłaniem stron na żądanie.

Wyczyszczenie strony obsługiwanej przez usuniętą pamięć

Rysunek 2. Wyczyszczona strona, obsługiwana przez pamięć, została usunięta

kswapd może przenosić buforowane prywatne brudne strony i anonimowe brudne strony do pamięci zRAM, gdzie są one kompresowane. Pozwala to zwolnić pamięć dostępną w pamięci RAM (wolne strony). Jeśli proces próbuje dotknąć brudnej strony w zRAM, jest ona rozpakowywana i przenoszona z powrotem do pamięci RAM. Jeśli proces powiązany z skompresowaną stroną zostanie przerwany, strona zostanie usunięta z zRAM-u.

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

Zanieczyszczona strona została przeniesiona do pamięci zRAM i skompresowana

Rysunek 3. Zanieczyszczona strona została przeniesiona do pamięci zRAM i skompresowana

Zabójca z powodu braku pamięci

Często kswapd nie może zwolnić wystarczającej ilości pamięci dla systemu. W takim przypadku system używa onTrimMemory(), aby powiadomić aplikację o tym, że ma mało pamięci i powinna zmniejszyć przydziały. Jeśli to nie wystarcza, jądro zaczyna zabijać procesy, aby zwolnić pamięć. Wykorzystuje do tego mechanizm LMK (Low-Memory Killer).

Aby zdecydować, który proces należy zakończyć, LMK używa wyniku „brak pamięci” o nazwie oom_adj_score, aby ustalić priorytety uruchomionych procesów. Procesy o wysokiej wartości są zabijane w pierwszej kolejności. Najpierw są zabijane aplikacje działające w tle, a na końcu procesy systemowe. W tabeli poniżej znajdziesz kategorie oceny LMK od najwyższej do najniższej. Elementy w kategorii o najwyższym wyniku w pierwszym wierszu zostaną odrzucone jako pierwsze:

Procesy Androida, rekordy u góry

Rysunek 4. procesy Androida, z wysokimi wynikami u góry i niskim u dołu;

Oto opisy poszczególnych kategorii w tabeli powyżej:

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

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

  • Aplikacja ekranu głównego: to aplikacja uruchamiająca. Za jej zamknięciem tapeta zniknie.

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

  • Aplikacje dające się zauważyć: aplikacje nieaktywne na pierwszym planie, które są w jakiś sposób zauważalne dla użytkownika, na przykład proces wyszukiwania wyświetlający mały interfejs użytkownika lub odtwarzanie muzyki.

  • Aplikacja na pierwszym planie: aplikacja, której obecnie używasz. Zamykanie aplikacji na pierwszym planie wygląda jak awaria aplikacji, która może wskazywać użytkownikowi, że coś jest nie tak z urządzeniem.

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

  • System: procesy systemowe. Gdy te procesy zostaną zatrzymane, telefon może się zresetować.

  • Natywna: procesy na bardzo niskim poziomie używane przez system (np. kswapd).

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

Obliczanie wykorzystania pamięci

Rdzeń śledzi wszystkie strony pamięci w systemie.

Strony używane przez różne procesy

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

Podczas określania ilości pamięci używanej przez aplikację system musi uwzględnić strony współdzielone. 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 poszczególnych aplikacji.

Strony udostępnione przez 2 aplikacje

Rysunek 6. Strony udostępnione przez 2 aplikacje (pośrodku)

Aby określić obciążenie pamięci aplikacji, możesz użyć dowolnego z tych parametrów:

  • Resident Set Size (RSS): liczba stron udostępnionych i nieudostępnionych używanych przez aplikację.
  • Rozmiar zbioru proporcjonalnego (PSS): liczba nieudostępnionych stron używanych przez aplikację oraz równomierne rozłożenie udostępnionych stron (np. jeśli 3 procesy udostępniają 3 MB, każdy z nich ma w ramach PSS po 1 MB).
  • Rozmiar unikalnego zbioru (USS): liczba stron nieudostępnionych, które są używane przez aplikację (nie uwzględnia stron udostępnionych).

PSS jest przydatna 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 ile procesów je współdzieli. RSS nie rozróżnia stron współdzielonych i nie współdzielonych (co przyspiesza obliczenia) oraz lepiej sprawdza się w śledzeniu zmian w przydzielaniu pamięci.

Dodatkowe materiały