Android Runtime (ART) i maszyna wirtualna Dalvik używają stronicowania i mapowania pamięci (mmapping) do zarządzania pamięcią. Oznacza to, że każda pamięć, którą aplikacja modyfikuje – czy to przez alokację nowych obiektów, czy dotykanie stron mmapped – pozostaje w pamięci RAM i nie może być stronicowana. Jedynym sposobem na zwolnienie pamięci z aplikacji jest zwolnienie referencji do obiektów, które aplikacja przechowuje, czyniąc pamięć dostępną dla garbage collectora. Jest to z jednym wyjątkiem: wszelkie pliki zmapowane bez modyfikacji, takie jak kod, mogą zostać wyrzucone z pamięci RAM, jeśli system chce użyć tej pamięci gdzie indziej.
Na tej stronie wyjaśniono, jak Android zarządza procesami aplikacji i alokacją pamięci. Aby uzyskać więcej informacji o tym, jak efektywniej zarządzać pamięcią w swojej aplikacji, zobacz Zarządzaj pamięcią swojej aplikacji.
Odśmiecanie
Zarządzane środowisko pamięci, jak ART lub maszyna wirtualna Dalvik, śledzi każdą alokację pamięci. Gdy stwierdzi, że fragment pamięci nie jest już używany przez program, zwalnia go z powrotem do sterty, bez żadnej interwencji programisty. Mechanizm odzyskiwania nieużywanej pamięci w zarządzanym środowisku pamięciowym znany jest jako garbage collection. Garbage collection ma dwa cele: znaleźć obiekty danych w programie, do których nie można uzyskać dostępu w przyszłości; i odzyskać zasoby używane przez te obiekty.
Sterta pamięci Androida jest generacyjna, co oznacza, że istnieją różne wiadra alokacji, które śledzi, w oparciu o oczekiwany czas życia i rozmiar przydzielanego obiektu. Na przykład, niedawno zaalokowane obiekty należą do generacji Young. Gdy obiekt pozostaje aktywny wystarczająco długo, może zostać awansowany do starszej generacji, a następnie do generacji stałej.
Każda generacja sterty ma swój własny górny limit ilości pamięci, którą mogą zajmować znajdujące się tam obiekty. Za każdym razem, gdy generacja zaczyna się zapełniać, system wykonuje zdarzenie garbage collection, próbując zwolnić pamięć. Czas trwania odśmiecania zależy od tego, które pokolenie obiektów jest zbierane i jak wiele aktywnych obiektów znajduje się w każdym pokoleniu.
Mimo że odśmiecanie może być dość szybkie, nadal może wpływać na wydajność Twojej aplikacji. W zasadzie nie masz wpływu na to, kiedy wystąpi zdarzenie odśmiecania z poziomu Twojego kodu. System posiada działający zestaw kryteriów określających, kiedy należy wykonać odśmiecanie. Gdy kryteria te zostaną spełnione, system zatrzymuje wykonywanie procesu i rozpoczyna zbieranie śmieci. Jeśli garbage collection występuje w środku intensywnej pętli przetwarzania, takiej jak animacja lub podczas odtwarzania muzyki, może to zwiększyć czas przetwarzania. Ten wzrost może potencjalnie spowodować przekroczenie zalecanego progu 16ms dla wydajnego i płynnego renderowania klatek.
Dodatkowo, Twój kod może wykonywać czynności, które wymuszają częstsze występowanie zdarzeń odśmiecania lub sprawiają, że trwają one dłużej niż normalnie. Na przykład, jeśli alokujesz wiele obiektów w najbardziej wewnętrznej części pętli for podczas każdej klatki animacji alpha blending, możesz zanieczyścić stertę pamięci dużą ilością obiektów. W takich okolicznościach, garbage collector wykonuje wiele zdarzeń garbage collection i może pogorszyć wydajność twojej aplikacji.
Więcej ogólnych informacji o garbage collection, zobacz Garbage collection.
Współdzielenie pamięci
Aby zmieścić wszystko, czego potrzebuje w RAM, Android próbuje dzielić strony RAM pomiędzy procesy. Może to robić na następujące sposoby:
- Każdy proces aplikacji jest rozwidlony z istniejącego procesu o nazwie Zygote. Proces Zygote jest uruchamiany podczas startu systemu i ładuje wspólny kod frameworka oraz zasoby (takie jak motywy aktywności). Aby uruchomić nowy proces aplikacji, system rozwidla proces Zygote, a następnie ładuje i uruchamia kod aplikacji w nowym procesie. Dzięki takiemu podejściu większość stron RAM przeznaczonych na kod i zasoby frameworka może być współdzielona przez wszystkie procesy aplikacji.
- Większość statycznych danych jest mmapowana do procesu. Ta technika pozwala na współdzielenie danych pomiędzy procesami, a także pozwala na ich stronicowanie w razie potrzeby. Przykładowe dane statyczne obejmują: Kod Dalvik (poprzez umieszczenie go we wstępnie powiązanym pliku
.odex
do bezpośredniego mmapowania), zasoby aplikacji (poprzez zaprojektowanie tabeli zasobów tak, aby była strukturą, którą można mmapować oraz poprzez wyrównanie wpisów zip w APK), oraz tradycyjne elementy projektu, takie jak natywny kod w plikach.so
. - W wielu miejscach, Android współdzieli tę samą dynamiczną pamięć RAM pomiędzy procesami używając jawnie przydzielonych regionów pamięci współdzielonej (albo z ashmem albo gralloc). Na przykład, powierzchnie okien używają pamięci współdzielonej między aplikacją a kompozytorem ekranu, a bufory kursora używają pamięci współdzielonej między dostawcą treści a klientem.
Ze względu na szerokie zastosowanie pamięci współdzielonej, określenie ilości pamięci używanej przez twoją aplikację wymaga ostrożności. Techniki prawidłowego określania wykorzystania pamięci przez aplikację są omówione w sekcji Investigating Your RAM Usage.
Allocate and reclaim app memory
Sterta Dalvik jest ograniczona do pojedynczego zakresu pamięci wirtualnej dla każdego procesu aplikacji. To definiuje logiczny rozmiar sterty, który może rosnąć w miarę potrzeb, ale tylko do limitu, który system definiuje dla każdej aplikacji.
Logiczny rozmiar sterty nie jest taki sam jak ilość pamięci fizycznej używanej przez stertę. Podczas sprawdzania sterty twojej aplikacji, Android oblicza wartość zwaną Proporcjonalnym Rozmiar zestawu (PSS), który uwzględnia zarówno brudne i czyste strony, które są współdzielone z innymi procesami – ale tylko w ilości, która jest proporcjonalna do tego, jak wiele aplikacji dzieli tę pamięć RAM. Ta suma (PSS) jest tym, co system uważa za ślad pamięci fizycznej użytkownika. Aby uzyskać więcej informacji o PSS, zobacz przewodnik Investigating Your RAM Usage.
Sterta Dalvik nie zagęszcza logicznego rozmiaru sterty, co oznacza, że Android nie defragmentuje sterty, aby zamknąć przestrzeń. Android może tylko zmniejszyć logiczny rozmiar sterty, gdy jest niewykorzystane miejsce na końcu sterty. Jednakże, system nadal może zmniejszyć pamięć fizyczną używaną przez stertę. Po zbieraniu śmieci Dalvik przemierza stertę i znajduje nieużywane strony, a następnie zwraca te strony do jądra za pomocą madvise. Tak więc, sparowane alokacje i deallokacje dużych kawałków powinny skutkować odzyskaniem całej (lub prawie całej) używanej pamięci fizycznej. Jednakże, odzyskiwanie pamięci z małych alokacji może być znacznie mniej wydajne, ponieważ strona użyta do małej alokacji może być nadal współdzielona z czymś innym, co nie zostało jeszcze uwolnione.
Ograniczyć pamięć aplikacji
Aby utrzymać funkcjonalne środowisko wielozadaniowe, Android ustawia twardy limit na rozmiar sterty dla każdej aplikacji. Dokładny limit wielkości sterty różni się pomiędzy urządzeniami w oparciu o to, ile pamięci RAM urządzenie ma dostępne ogółem. Jeśli twoja aplikacja osiągnęła pojemność sterty i próbuje przydzielić więcej pamięci, może otrzymać OutOfMemoryError
.
W niektórych przypadkach możesz chcieć zapytać system, aby dokładnie określić, ile miejsca na stercie masz dostępne na bieżącym urządzeniu – na przykład, aby określić, ile danych można bezpiecznie przechowywać w pamięci podręcznej. Możesz zapytać system o tę liczbę poprzez wywołanie getMemoryClass()
. Ta metoda zwraca liczbę całkowitą wskazującą liczbę megabajtów dostępnych dla sterty twojej aplikacji.
Przełączanie aplikacji
Kiedy użytkownicy przełączają się między aplikacjami, Android zachowuje aplikacje, które nie są na pierwszym planie – to znaczy, nie są widoczne dla użytkownika lub uruchamiają usługę na pierwszym planie, taką jak odtwarzanie muzyki – w pamięci podręcznej. Na przykład, gdy użytkownik po raz pierwszy uruchamia aplikację, tworzony jest dla niej proces, ale gdy użytkownik opuszcza aplikację, proces ten nie kończy się. System przechowuje ten proces w pamięci podręcznej. Jeśli użytkownik później wróci do aplikacji, system ponownie wykorzysta ten proces, dzięki czemu aplikacja będzie przełączać się szybciej.
Jeśli Twoja aplikacja ma buforowany proces i zachowuje zasoby, których aktualnie nie potrzebuje, wtedy Twoja aplikacja – nawet gdy użytkownik jej nie używa – wpływa na ogólną wydajność systemu. Gdy systemowi kończą się zasoby, takie jak pamięć, zabija procesy w pamięci podręcznej. System uwzględnia również procesy, które zajmują najwięcej pamięci i może je zakończyć, aby zwolnić pamięć RAM.
Uwaga: Im mniej pamięci zużywa twoja aplikacja podczas przebywania w pamięci podręcznej, tym większe są jej szanse, że nie zostanie zabita i będzie mogła szybko wznowić działanie. Jednakże, w zależności od bieżących wymagań systemowych, możliwe jest, że zbuforowane procesy zostaną zakończone w dowolnym momencie bez względu na ich wykorzystanie zasobów.
Więcej informacji o tym, jak procesy są buforowane, gdy nie działają na pierwszym planie i jak Android decyduje, które z nich mogą zostać zabite, znajdziesz w przewodniku Procesy i wątki.