Omówienie zarządzania pamięcią

Środowisko wykonawcze Android (ART) i maszyna wirtualna Dalvik zarządzają pamięcią za pomocą stronicowania i mapowania pamięci (mmappingu). Oznacza to, że każda pamięć, którą zmienia aplikacja (przez przydzielanie nowych obiektów lub dotykanie zmapowanych stron), pozostaje w pamięci RAM i nie można jej wyodrębnić. Jedynym sposobem na zwolnienie pamięci z aplikacji jest zwolnienie odwołań do obiektów przechowywanych przez aplikację, dzięki czemu pamięć stanie się dostępna dla modułu czyszczenia pamięci. Jest 1 wyjątek: wszystkie pliki bez modyfikacji (np. kod) mogą zostać wyczyszczone z pamięci RAM, jeśli system chce jej użyć gdzie indziej.

Na tej stronie wyjaśniamy, jak Android zarządza procesami aplikacji i alokacją pamięci. Więcej informacji o sprawnym zarządzaniu pamięcią w aplikacji znajdziesz w sekcji Zarządzanie pamięcią aplikacji.

Wywóz śmieci

Zarządzane środowisko pamięci, takie jak maszyna wirtualna ART lub Dalvik, śledzi każdą alokację pamięci. Po stwierdzeniu, że jakaś pamięć nie jest już używana przez program, uwolni ją z powrotem do stosu (bez udziału programisty). Mechanizm odzyskiwania niewykorzystanej pamięci w zarządzanym środowisku pamięci nazywany jest zbieraniem pamięci. Czyszczenie pamięci ma 2 cele: znajdowanie w programie obiektów danych, do których w przyszłości nie będzie można uzyskać dostępu, oraz odzyskiwanie zasobów używanych przez te obiekty.

Stos pamięci Androida ma charakter generacyjny, co oznacza, że śledzi różne zasobniki alokacji w zależności od oczekiwanego czasu życia i rozmiaru przydzielanego obiektu. Na przykład ostatnio przydzielone obiekty należą do młodej generacji. Jeśli obiekt pozostaje aktywny wystarczająco długo, może zostać awansowany do starszej generacji, a następnie zostanie wygenerowany na stałe.

Każda generacja sterty ma własny górny limit ilości pamięci, którą mogą zajmować obiekty. Za każdym razem, gdy generacja rozpoczyna wypełnianie, system wykonuje zdarzenie czyszczenia pamięci, aby zwolnić pamięć. Czas trwania czyszczenia pamięci zależy od tego, którą generację gromadzi obiekty i ilu aktywnych obiektów jest w każdej generacji.

Usuwanie pamięci może być dość szybkie, ale nadal może wpływać na wydajność aplikacji. Zasadniczo nie masz wpływu na to, kiedy zdarzenie czyszczenia pamięci ma miejsce z poziomu kodu. System ma aktywny zestaw kryteriów określających, kiedy ma zostać odśnieżony. Gdy kryteria będą spełnione, system przerywa wykonywanie procesu i rozpoczyna czyszczenie pamięci. Jeśli zbieranie danych do śmieci odbywa się w trakcie intensywnej pętli przetwarzania, np. podczas odtwarzania animacji, lub podczas odtwarzania muzyki, może wydłużyć czas przetwarzania. Może to spowodować, że wykonanie kodu w aplikacji przekroczy zalecany próg 16 ms, aby zapewnić efektywne i płynne renderowanie klatek.

Poza tym przepływ kodu może wykonywać działania, które wymuszają częstsze występowanie zdarzeń czyszczenia pamięci lub wydłużanie ich czasu trwania. Jeśli na przykład podczas każdej klatki animacji mieszania alfa przydzielisz wiele obiektów w najbardziej wewnętrznej części pętli for, może to spowodować zanieczyszczenie stosu pamięci dużą liczbą obiektów. W takiej sytuacji kolektor śmieci wykonuje wiele zdarzeń czyszczenia pamięci, co może obniżać wydajność aplikacji.

Ogólne informacje o wybieraniu odpadów znajdziesz w artykule na temat zbierania śmieci.

Udostępnij wspomnienie

Aby zaspokoić wszystkie potrzeby w pamięci RAM, Android próbuje współdzielić strony pamięci RAM pomiędzy procesami. Może to zrobić na następujące sposoby:

  • Każdy proces aplikacji jest rozwidlony z istniejącego procesu o nazwie Zygote. Proces Zygote rozpoczyna się podczas uruchamiania systemu i wczytywania wspólnego kodu platformy oraz zasobów (takich jak motywy aktywności). Aby rozpocząć nowy proces aplikacji, system tworzy rozwidlenie procesu Zygote, a następnie wczytuje i uruchamia kod aplikacji w nowym procesie. To podejście umożliwia współdzielenie większości stron RAM przydzielonych na potrzeby kodu platformy i zasobów przez wszystkie procesy aplikacji.
  • Większość danych statycznych jest przekazywana w proces. Ta metoda umożliwia udostępnianie danych między procesami, a także dzielenie ich na strony w razie potrzeby. Przykładowe dane statyczne obejmują: kod Dalvik (umieszczając go w wstępnie połączonym pliku .odex na potrzeby bezpośredniego mmappingu), zasoby aplikacji (przez zaprojektowanie tabeli zasobów w taki sposób, aby mogła być skompresowana przez dopasowanie wpisów pocztowych w pliku APK) oraz tradycyjne elementy projektu, takie jak kod natywny w plikach .so.
  • W wielu miejscach Android współdzieli tę samą dynamiczną pamięć RAM przez procesy, korzystając z jawnie przydzielonych regionów pamięci (Ashmem lub Gralloc). Na przykład powierzchnie okien używają pamięci współdzielonej między aplikacją a kompozytorem ekranu, a bufory kursorów używają pamięci współdzielonej między dostawcą treści a klientem.

Ze względu na duże wykorzystanie pamięci współdzielonej należy dokładnie określić, ile pamięci używa. Metody poprawnego określania wykorzystania pamięci przez aplikację zostały omówione w artykule Badanie wykorzystania pamięci RAM.

Przydzielanie i odzyskiwanie pamięci aplikacji

Sterta Dalvik jest ograniczona do 1 zakresu pamięci wirtualnej dla każdego procesu aplikacji. Określa logiczny rozmiar stosu, który może rosnąć w miarę potrzeb, ale do limitu zdefiniowanego przez system dla każdej aplikacji.

Logiczny rozmiar sterty nie jest taki sam jak ilość pamięci fizycznej używanej przez stertę. Podczas sprawdzania stosu aplikacji Android oblicza wartość tzw. proporcjonalnego rozmiaru zestawu (PSS), która uwzględnia zarówno uszkodzone, jak i czyste strony udostępniane innym procesom, ale tylko w ilości proporcjonalnej do liczby aplikacji korzystających z tej pamięci RAM. Ta łączna wartość (PSS) jest obliczana przez system za wykorzystanie pamięci fizycznej. Więcej informacji o PSS znajdziesz w przewodniku Sprawdzanie wykorzystania pamięci RAM.

Stos Dalvik nie zmniejsza logicznej wielkości stosu, co oznacza, że Android nie defragmentuje sterty, aby zmniejszyć jej rozmiar. Android może zmniejszać rozmiar sterty logicznej tylko wtedy, gdy na jej końcu znajduje się niewykorzystane miejsce. System może jednak nadal zmniejszyć ilość pamięci fizycznej używanej przez stertę. Po odczyszczeniu pamięci Dalvik znajduje nieużywane strony, a następnie zwraca je do jądra za pomocą algorytmu madvis. Z tego względu połączone przydziały i przydziały dużych fragmentów powinny spowodować odzyskanie całej (lub prawie całości) używanej pamięci fizycznej. Jednak odzyskiwanie pamięci z niewielkich przydziałów może być znacznie mniej wydajne, ponieważ strona używana na potrzeby niewielkiego przydziału może nadal być współużytkowana z innym elementem, który nie został jeszcze zwolniony.

Ogranicz pamięć aplikacji

Aby utrzymać sprawne środowisko wielozadaniowe, Android ustala stały limit rozmiaru stosu dla każdej aplikacji. Dokładny limit rozmiaru stosu różni się w zależności od urządzenia i jego ogólnej ilości pamięci RAM. Jeśli aplikacja osiągnęła już limit sterty i spróbuje przydzielić więcej pamięci, może otrzymać OutOfMemoryError.

W niektórych przypadkach możesz chcieć wysłać do systemu zapytanie, aby dokładnie określić ilość miejsca na stercie dostępnej na bieżącym urządzeniu – na przykład aby ustalić, ile danych można bezpiecznie przechowywać w pamięci podręcznej. Możesz wysłać zapytanie do systemu o tę liczbę, wywołując getMemoryClass(). Ta metoda zwraca liczbę całkowitą wskazującą liczbę megabajtów dostępnych dla sterty aplikacji.

Przełączanie aplikacji

Gdy użytkownicy przełączają się między aplikacjami, Android przechowuje aplikacje spoza pierwszego planu, czyli niewidoczne dla użytkownika lub działające na pierwszym planie, np. usługi do odtwarzania muzyki, w pamięci podręcznej. Na przykład, gdy użytkownik po raz pierwszy uruchomi aplikację, tworzony jest dla niej proces, ale gdy użytkownik opuści aplikację, proces nie zatrzymuje się. System zapisuje proces w pamięci podręcznej. Jeśli później użytkownik wróci do aplikacji, system użyje go ponownie, co sprawi, że aplikacja przełączy się szybciej.

Jeśli aplikacja ma proces w pamięci podręcznej, który przechowuje zasoby, których obecnie nie potrzebuje, to nawet wtedy, gdy użytkownik jej nie używa, wpływa na ogólną wydajność systemu. Gdy w systemie zaczyna brakować zasobów takich jak pamięć, kończy się procesy w pamięci podręcznej. System uwzględnia również procesy, które zajmują najwięcej pamięci i mogą je zamknąć, aby zwolnić pamięć RAM.

Uwaga: im mniej pamięci zużywa aplikacja w pamięci podręcznej, tym większe szanse na jej zamknięcie i szybkie wznowienie działania. Jednak w zależności od błyskawicznych wymagań systemowych możliwe jest jednak zamknięcie procesów z pamięci podręcznej w dowolnym momencie, bez względu na wykorzystanie zasobów.

Więcej informacji o sposobie buforowania procesów, gdy nie działają na pierwszym planie, oraz o tym, jak Android decyduje, które z nich można zakończyć, znajdziesz w przewodniku na temat procesów i wątków.

Test pamięci masowej

Mimo że problemy z wykorzystaniem pamięci masowej są mniej powszechne na zaawansowanych urządzeniach, nadal mogą powodować problemy u użytkowników urządzeń z małą ilością pamięci RAM, np. z Androidem (wersja Go). Warto spróbować odtworzyć środowisko przeciążone pamięcią, aby móc napisać testy z instrumentacją, które pozwolą zweryfikować działanie aplikacji i poprawić wygodę użytkowników korzystających z urządzeń z małą ilością pamięci.

Test stresu aplikacji

Stressful Application Test (stressapptest) to test interfejsu pamięci, który pomaga stworzyć realistyczne sytuacje wymagające dużego obciążenia, aby przetestować różne ograniczenia pamięci i sprzętu aplikacji. Dzięki możliwości definiowania ograniczeń związanych z czasem i pamięcią można stworzyć narzędzia do weryfikowania rzeczywistych spotkań z dużą ilością pamięci. Na przykład użyj poniższego zestawu poleceń, aby przekazać bibliotekę statyczną do systemu plików danych, udostępnić ją jako plik wykonywalny i przeprowadzić test obciążeniowy na 20 sekund z 990 MB:
    adb push stressapptest /data/local/tmp/
    adb shell chmod 777 /data/local/tmp/stressapptest
    adb shell /data/local/tmp/stressapptest -s 20 -M 990

  

Więcej informacji o instalowaniu narzędzia, typowych argumentach i obsłudze błędów znajdziesz w dokumentacji stressapptest.

Obserwacje dotyczące testu aplikacjiSpresapptest

Możesz używać narzędzi takich jak stressapptest do wysyłania żądań dotyczących przydziałów pamięci większych niż jest dostępne bezpłatnie. Prośby tego typu mogą powodować wyświetlanie różnych alertów, o których trzeba pamiętać po stronie programistycznej. Z powodu małej dostępności pamięci mogą wystąpić 3 główne alerty:
  • SIGABRT: jest to krytyczne awaria natywna w procesie, ponieważ żądana jest alokacja o rozmiarze większym niż bezpłatna pamięć, w czasie gdy w systemie niekorzystnie wpływa już niekorzystna ilość pamięci.
  • SIGQUIT: generuje zrzut podstawowej pamięci i kończy proces po wykryciu przez test z instrumentacją.
  • TRIM_MEMORY_EVENTS: te wywołania zwrotne są dostępne w Androidzie 4.1 (poziom interfejsu API 16) i nowszych i udostępniają szczegółowe alerty dotyczące pamięci dla Twojego procesu.