Na tej stronie dowiesz się, jak proaktywnie zmniejszyć zużycie pamięci w aplikacji. For informacje o tym, jak system operacyjny Android zarządza pamięcią, zobacz Omówienie zarządzania pamięcią.
Pamięć RAM jest cennym zasobem w każdym środowisku programistycznym, a w przypadku mobilnego systemu operacyjnego, w którym pamięć fizyczna jest często ograniczona, jest jeszcze cenniejsza. Zarówno środowisko wykonawcze Androida (ART), jak i maszyna wirtualna Dalvik wykonują rutynowe odśmiecanie pamięci, ale nie oznacza to, że możesz ignorować, kiedy i gdzie aplikacja przydziela i zwalnia pamięć. Nadal
musisz unikać wprowadzania wycieków pamięci – zwykle spowodowanych przechowywaniem
odwołań do obiektów w statycznych zmiennych składowych – i zwalniać obiekty Reference
w odpowiednim czasie, zgodnie z wywołaniami zwrotnymi cyklu życia.
Zmniejszanie rozmiaru kodu i zasobów aplikacji
Niektóre zasoby i biblioteki w kodzie mogą zużywać pamięć bez Twojej wiedzy. Ogólny rozmiar aplikacji, w tym bibliotek innych firm lub zasobów osadzonych, może wpływać na ilość pamięci zużywanej przez aplikację. Możesz zmniejszyć zużycie pamięci przez aplikację, usuwając z kodu zbędne, niepotrzebne lub nadmiernie rozbudowane komponenty, zasoby i biblioteki.
Zmniejszanie ogólnego rozmiaru aplikacji przez włączenie R8
Skompilowany kod aplikacji jest aktywną częścią zużycia pamięci w czasie działania. Każda klasa, metoda, zależność biblioteki i stała tekstowa musi zostać wczytana do pamięci RAM podczas uruchamiania. Im większa skompilowana baza kodu, tym więcej fizycznej pamięci RAM wymaga aplikacja.
Aby zmniejszyć wykorzystanie pamięci przez aplikację, możesz użyć R8. R8 jest tradycyjnie znany z zmniejszania rozmiaru APK, ale ma bezpośredni, pozytywny wpływ na pamięć w czasie działania (RAM). R8 analizuje kod bajtowy aplikacji, aby usunąć martwy kod, scalić zbędne klasy, wstawić metody i zminimalizować identyfikatory. Dzięki wczytywaniu do pamięci RAM mniejszej ilości skompilowanego kodu bajtowego z pliku APK zmniejsza się ogólne wykorzystanie pamięci przez aplikację. Dodatkowo minimalizowanie nazw klas, metod i pól do krótszych identyfikatorów bezpośrednio zmniejsza obciążenie pamięci RAM. Optymalizacje takie jak scalanie klas i rozbudowane wstawianie metod zastępują też kosztowne wyszukiwania w czasie działania i wzorce przydzielania, co skutkuje zoptymalizowaną pamięcią sterty i stosu.
Informacje o regułach zachowywania
Reguły zachowywania to instrukcje konfiguracyjne, które informują R8, które części kodu mają zostać zachowane podczas optymalizacji, co uniemożliwia usunięcie lub zminimalizowanie kodu, na którym opiera się aplikacja. Więcej informacji znajdziesz w artykule Omówienie reguł zachowywania.
Źle napisane reguły zachowywania uniemożliwiają R8 optymalizację dużych części bazy kodu. Unikaj zbyt szerokich reguł zachowywania i postępuj zgodnie z tymi sprawdzonymi metodami:
- Reguły globalne, których należy unikać:
-dontoptimize: całkowicie wyłącza optymalizację całej aplikacji, co skutkuje większymi i wolniejszymi plikami wykonywalnymi.-dontshrink: uniemożliwia usuwanie nieużywanego kodu i zasobów.-dontobfuscate: uniemożliwia minimalizowanie nazw, co uniemożliwia oszczędzanie pamięci (szczególnie w przypadku dużych aplikacji).
Unikaj symboli wieloznacznych w całym pakiecie: szerokie reguły, takie jak
-keep class com.example.package.** { *; }, wymuszają na R8 zachowanie każdej klasy, pola i metody w tym pakiecie. Całkowicie uniemożliwia to R8 usuwanie, optymalizowanie lub minimalizowanie kodu w tym pakiecie.Używaj domyślnego pliku konfiguracyjnego R8: zawsze używaj pliku
proguard-android-optimize.txt.
Więcej informacji o pisaniu reguł zachowywania znajdziesz w artykule Omówienie reguł zachowywania. Konkretne wzorce, których należy używać i których należy unikać, znajdziesz w artykule Sprawdzone metody dotyczące reguł zachowywania.
Analizator konfiguracji R8 zawiera informacje o konfiguracji R8 i o tym, jak każda reguła zachowywania wpływa na aplikację. Więcej informacji o tym, jak identyfikować reguły, które blokują optymalizację, znajdziesz w artykule Analizator konfiguracji R8.
Uważaj na korzystanie z bibliotek zewnętrznych
Kod biblioteki zewnętrznej często nie jest pisany pod kątem środowisk mobilnych i może być nieefektywny w przypadku pracy na kliencie mobilnym. Gdy używasz biblioteki zewnętrznej, może być konieczne zoptymalizowanie jej pod kątem urządzeń mobilnych. Zaplanuj tę pracę z wyprzedzeniem i przed użyciem przeanalizuj bibliotekę pod kątem rozmiaru kodu i zużycia pamięci RAM.
Nawet niektóre biblioteki zoptymalizowane pod kątem urządzeń mobilnych mogą powodować problemy ze względu na różne implementacje. Na przykład jedna biblioteka może używać lite protobufs, a inna – micro protobufs, co powoduje, że w aplikacji znajdują się 2 różne implementacje protobufs. Może się to zdarzyć w przypadku różnych implementacji logowania, analiz, frameworków wczytywania obrazów, buforowania i wielu innych rzeczy, których się nie spodziewasz.
Optymalizowanie aplikacji za pomocą R8 może usunąć nieużywany kod z zależności, ale jego skuteczność jest często ograniczona przez wewnętrzną konfigurację biblioteki. Na przykład szerokie reguły zachowywania lub użycie refleksji w bibliotece mogą uniemożliwić R8 zmniejszenie jej kodu, co prowadzi do większego zużycia pamięci. Strategie dotyczące wybierania wydajnych bibliotek znajdziesz w artykule Wybieranie bibliotek.
Unikaj używania biblioteki wspólnej tylko do 1 lub 2 funkcji spośród kilkudziesięciu. Nie używaj dużej ilości kodu i obciążenia, których nie używasz. Zastanawiając się, czy użyć biblioteki, poszukaj implementacji, która ściśle odpowiada Twoim potrzebom. W przeciwnym razie możesz zdecydować się na utworzenie własnej implementacji.
Używanie Hilt lub Dagger 2 do wstrzykiwania zależności
Frameworki wstrzykiwania zależności mogą uprościć pisany kod i zapewnić adaptacyjne środowisko, które jest przydatne do testowania i innych zmian konfiguracji.
Jeśli zamierzasz używać w aplikacji frameworka wstrzykiwania zależności, rozważ użycie Hilt lub Dagger. Hilt to biblioteka wstrzykiwania zależności na Androida, która działa na podstawie Dagger. Dagger nie używa refleksji do skanowania kodu aplikacji. W aplikacjach na Androida możesz używać statycznej implementacji Dagger w czasie kompilacji bez niepotrzebnych kosztów w czasie działania ani wykorzystania pamięci.
Inne frameworki wstrzykiwania zależności, które używają refleksji, inicjują procesy, skanując kod pod kątem adnotacji. Ten proces może wymagać znacznie większej liczby cykli procesora i pamięci RAM oraz powodować zauważalne opóźnienie podczas uruchamiania aplikacji.
Podczas korzystania z wstrzykiwania zależności uważaj, aby uniknąć wycieków pamięci, zapewniając odpowiedni zakres obiektów. Przechowywanie obiektów dłużej niż jest to konieczne przez powiązanie ich z nieprawidłowym cyklem życia może prowadzić do wycieków pamięci.
Uważaj na wczytywanie obrazów
Bitmapy graficzne są zwykle największymi wspólnymi obiektami znajdującymi się w pamięci aplikacji. Nawet jeśli pracujesz ze skompresowanymi plikami, takimi jak JPEG, plik musi zostać rozpakowany do nieskompresowanej bitmapy, aby można go było wyświetlić na ekranie. Mały skompresowany plik obrazu może zostać rozpakowany do bardzo dużej bitmapy.
Na przykład większość bitmap używa konfiguracji ARGB_8888, co oznacza, że każdy piksel wymaga 4 bajtów pamięci – po 1 bajcie na kolor czerwony, zielony, niebieski i alfa (przezroczystość). Jeśli masz plik JPEG o rozmiarze 100 KB i wyświetlasz go w widoku o wymiarach 1000 × 1000 pikseli, bitmapa będzie wymagać 4 bajtów na każdy z tych 1 000 000 pikseli, co daje łącznie 4 MB pamięci.
Aby zoptymalizować użycie obrazów, możesz zrobić kilka rzeczy. Na przykład używanie bibliotek wczytywania obrazów może pomóc w zwalnianiu pamięci, gdy nie jest ona potrzebna. Informacje o tym, jak efektywnie obsługiwać obrazy, znajdziesz w artykule Optymalizowanie bitmap obrazów.
Monitorowanie dostępnej pamięci i wykorzystania pamięci
Zanim rozwiążesz problemy z wykorzystaniem pamięci przez aplikację, musisz je znaleźć. Profiler pamięci w Android Studio pomaga znajdować i diagnozować problemy z pamięcią na te sposoby:
- Sprawdzanie, jak aplikacja przydziela pamięć w czasie. Profiler pamięci pokazuje wykres w czasie rzeczywistym, który przedstawia, ile pamięci używa aplikacja, liczbę przydzielonych obiektów Java i kiedy następuje odśmiecanie pamięci.
- Inicjowanie zdarzeń odśmiecania pamięci i robienie zrzutu sterty Java podczas działania aplikacji.
- Rejestrowanie przydziałów pamięci aplikacji, sprawdzanie wszystkich przydzielonych obiektów, wyświetlanie zrzutu stosu dla każdego przydziału i przechodzenie do odpowiedniego kodu w edytorze Android Studio.
Profiler pamięci jest też zintegrowany z biblioteką wykrywania wycieków LeakCanary. Dzięki LeakCanary możesz przenieść analizę wycieków pamięci z urządzenia testowego na komputer używany do programowania, co może znacznie przyspieszyć przepływ pracy. Więcej informacji znajdziesz w informacjach o wersji Android Studio.
Do diagnozowania problemów z pamięcią na podstawie danych od użytkowników korzystających z aplikacji w wersji produkcyjnej możesz używać innych narzędzi:
- Używaj Android Vitals do śledzenia zdarzeń LMK (low memory kill).
- Używaj Menedżera profilowania do śledzenia błędów braku pamięci oraz nietypowego zachowania aplikacji, które może być spowodowane wyciekami pamięci.
Zwalnianie pamięci w odpowiedzi na zdarzenia
W razie potrzeby Android może odzyskać pamięć z aplikacji lub całkowicie ją zatrzymać
aby zwolnić pamięć na potrzeby zadań krytycznych, jak opisano w artykule Omówienie zarządzania
pamięcią. Aby dodatkowo zrównoważyć pamięć systemową i uniknąć konieczności zatrzymania procesu aplikacji przez system, możesz zaimplementować interfejs ComponentCallbacks2
w klasach Activity. Dostarczona onTrimMemory()
metoda wywołania zwrotnego powiadamia aplikację o zdarzeniach związanych z cyklem życia lub pamięcią, które
stanowią dobrą okazję do dobrowolnego zmniejszenia wykorzystania pamięci przez aplikację.
Zwalnianie pamięci może zmniejszyć częstotliwość zamykania aplikacji przez
proces zamykania aplikacji z powodu braku pamięci.
Implementacja onTrimMemory() powinna koncentrować się wyłącznie na zdarzeniach TRIM_MEMORY_UI_HIDDEN i TRIM_MEMORY_BACKGROUND. (Od Androida 14 system nie dostarcza już powiadomień o innych, starszych stałych. Te stałe zostały formalnie wycofane w Androidzie 15).
TRIM_MEMORY_UI_HIDDEN: ten sygnał wskazuje, że interfejs aplikacji został przeniesiony poza widok użytkownika. To przejście daje możliwość zwolnienia znacznych przydziałów pamięci ściśle związanych z interfejsem, takich jak bitmapy, bufory odtwarzania wideo czy złożone zasoby animacji.TRIM_MEMORY_BACKGROUND: ten sygnał wskazuje, że proces znajduje się w tle i jest teraz kandydatem do zakończenia, aby zaspokoić globalne potrzeby systemu w zakresie pamięci. Aby wydłużyć czas, przez jaki proces pozostaje w stanie buforowanym, i zmniejszyć liczbę zimnych startów aplikacji, należy agresywnie zwalniać wszystkie zasoby, które można łatwo odtworzyć, gdy użytkownik wznowi sesję.
Ten przykładowy kod pokazuje, jak zaimplementować wywołanie zwrotne onTrimMemory() w celu reagowania na różne zdarzenia związane z pamięcią:
Kotlin
import android.content.ComponentCallbacks2
// Other import statements.
class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
// Other activity code.
/**
* Release memory when the UI becomes hidden or when system resources become low.
* @param level the memory-related event that is raised.
*/
override fun onTrimMemory(level: Int) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// Release memory related to UI elements, such as bitmap caches.
}
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Release memory related to background processing, such as by
// closing a database connection.
}
}
}
Java
import android.content.ComponentCallbacks2;
// Other import statements.
public class MainActivity extends AppCompatActivity
implements ComponentCallbacks2 {
// Other activity code.
/**
* Release memory when the UI becomes hidden or when system resources become low.
* @param level the memory-related event that is raised.
*/
public void onTrimMemory(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// Release memory related to UI elements, such as bitmap caches.
}
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Release memory related to background processing, such as by
// closing a database connection.
}
}
}
Sprawdzanie, ile pamięci potrzebujesz
Aby umożliwić działanie wielu procesów, Android ustawia limit rozmiaru sterty przydzielonej każdej aplikacji. Dokładny limit rozmiaru sterty różni się w zależności od urządzenia i ilości dostępnej pamięci RAM. Jeśli aplikacja osiągnie pojemność sterty
i spróbuje przydzielić więcej pamięci, system zgłosi błąd
OutOfMemoryError.
Aby uniknąć braku pamięci, możesz wysłać zapytanie do systemu, aby określić, ile miejsca na stercie jest dostępne na bieżącym urządzeniu. Aby uzyskać tę
wartość, możesz wysłać zapytanie do systemu, wywołując metodę getMemoryInfo(). Zwraca ona obiekt
ActivityManager.MemoryInfo, który zawiera informacje o
bieżącym stanie pamięci urządzenia, w tym o dostępnej pamięci, całkowitej pamięci i
progu pamięci – poziomie pamięci, przy którym system zaczyna zatrzymywać
procesy. Obiekt ActivityManager.MemoryInfo udostępnia też
lowMemory, która jest prostą wartością logiczną informującą, czy na urządzeniu
jest mało pamięci.
Poniższy przykładowy fragment kodu pokazuje, jak używać metody getMemoryInfo() w aplikacji.
Kotlin
fun doSomethingMemoryIntensive() {
// Before doing something that requires a lot of memory,
// check whether the device is in a low memory state.
if (!getAvailableMemory().lowMemory) {
// Do memory intensive work.
}
}
// Get a MemoryInfo object for the device's current memory status.
private fun getAvailableMemory(): ActivityManager.MemoryInfo {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return ActivityManager.MemoryInfo().also { memoryInfo ->
activityManager.getMemoryInfo(memoryInfo)
}
}
Java
public void doSomethingMemoryIntensive() {
// Before doing something that requires a lot of memory,
// check whether the device is in a low memory state.
ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();
if (!memoryInfo.lowMemory) {
// Do memory intensive work.
}
}
// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
return memoryInfo;
}
Monitorowanie zamykania aplikacji z powodu braku pamięci
Zamykanie aplikacji z powodu braku pamięci widoczne dla użytkowników występuje, gdy ilość pamięci systemowej jest krytycznie niska. Gdy pamięci jest mało, proces
lmkd (demon zamykania aplikacji z powodu braku pamięci) kończy procesy na podstawie ich wartości oom_adj_score. Aplikacje, które są buforowane lub uruchamiają usługę bez powiązanego interfejsu (np. zadanie), mają najwyższe wyniki i są zamykane jako pierwsze. Jeśli ilość pamięci nadal jest krytycznie niska, demon jest zmuszony do odzyskania pamięci z procesów z wartością oom_adj_score równą 0.
Ponieważ ta wartość jest zarezerwowana dla widocznych aplikacji, ich zakończenie powoduje natychmiastowe, niegrzeczne wyjście z procesu. Dla użytkownika końcowego wygląda to tak, jakby aplikacja się zawiesiła, często pomijając standardowe mechanizmy zapisywania stanu cyklu życia i powodując utratę postępów użytkownika.
Zamykanie procesów działających na pierwszym planie jest głównym celem Android Vitals, ponieważ służy jako proxy o wysokiej wierności w przypadku nieprawidłowego zarządzania pamięcią. Współczynnik LMK wyższy niż 1% wskazuje na pilną potrzebę natychmiastowego działania, ale niski współczynnik niekoniecznie oznacza, że wszystko jest w porządku. Niski współczynnik LMK widoczny dla użytkowników może oznaczać, że demon LMK często zamyka procesy, gdy działają one w tle, co pogarsza wydajność „uruchamiania częściowo z pamięci” i płynność wielozadaniowości. Dlatego zalecamy przestrzeganie sprawdzonych metod dotyczących pamięci niezależnie od bieżącego wyniku LMK, aby zapewnić długoterminową stabilność i kondycję urządzenia.
Używanie ProfilingManager do śledzenia problemów z pamięcią
Platforma Android udostępnia
ProfilingManager, zaawansowany interfejs API do obserwacji, który umożliwia rejestrowanie danych użytkowników w wersji produkcyjnej na podstawie
ustawionych przez Ciebie wyzwalaczy. Może to pomóc w identyfikowaniu trudnych do odtworzenia problemów z pamięcią.
Dwa nowe wyzwalacze wprowadzone w Androidzie 17 są szczególnie przydatne do wykrywania problemów z pamięcią:
TRIGGER_TYPE_OOMwskazuje, że aplikacja zgłosiła błądOutOfMemoryError. Wyzwalacz uruchamia się przy następnym uruchomieniu aplikacji po awarii, gdy aplikacja zarejestruje się w wyzwalaczach profilowania.TRIGGER_TYPE_ANOMALYuruchamia się, gdy system wykryje nietypowe zachowanie aplikacji. Może to być spowodowane m.in. nadmiernym wykorzystaniem pamięci. Wyzwalacz uruchamia się po tym, jak aplikacja wykazuje nadmierne wykorzystanie pamięci, i zanim system podejmie jakiekolwiek działania w celu zatrzymania procesu powodującego problem. Jeśli na przykład aplikacja przekroczy limity pamięci wprowadzone w Androidzie 17,TRIGGER_TYPE_ANOMALYuruchomi się, zanim system zamknie aplikację.
Więcej informacji o używaniu ProfilingManager do programowego rejestrowania
i pobierania wyzwalaczy znajdziesz w dokumentacji profilowania opartego na wyzwalaczach.
Możesz też używać profilowania opartego na aplikacji, aby ręcznie definiować punkty początkowe i końcowe śledzenia. Zalecamy to, aby ręcznie rejestrować zrzuty sterty lub profile sterty w obszarach, w których podejrzewasz wycieki pamięci lub nadmierne wykorzystanie pamięci.
Używanie bardziej wydajnych konstrukcji kodu
Niektóre funkcje Androida, klasy Java i konstrukcje kodu zużywają więcej pamięci niż inne. Możesz zminimalizować ilość pamięci używanej przez aplikację, wybierając w kodzie bardziej wydajne alternatywy.
Oszczędne używanie usług
Zdecydowanie zalecamy, aby nie pozostawiać usług działających, gdy nie jest to konieczne. Pozostawianie niepotrzebnych usług działających jest jednym z najgorszych błędów w zarządzaniu pamięcią, jakie może popełnić aplikacja na Androida. Jeśli aplikacja potrzebuje usługi do działania w tle, nie pozostawiaj jej uruchomionej, chyba że musi ona wykonywać zadanie. Zatrzymaj usługę, gdy zakończy zadanie. W przeciwnym razie możesz spowodować wyciek pamięci.
Gdy uruchamiasz usługę, system woli utrzymywać proces tej usługi w stanie działania. To zachowanie sprawia, że procesy usług są bardzo kosztowne, ponieważ pamięć RAM używana przez usługę pozostaje niedostępna dla innych procesów. Zmniejsza to liczbę buforowanych procesów, które system może przechowywać w pamięci podręcznej LRU, co zmniejsza wydajność przełączania aplikacji. Może to nawet prowadzić do thrashingu w systemie, gdy pamięci jest mało, a system nie może utrzymać wystarczającej liczby procesów do obsługi wszystkich aktualnie działających usług.
Ogólnie unikaj używania trwałych usług ze względu na ciągłe zapotrzebowanie na dostępną pamięć. Zamiast tego zalecamy używanie alternatywnej
implementacji, np. WorkManager.
Więcej informacji o tym, jak używać WorkManager do planowania procesów w tle
, znajdziesz w artykule Praca trwała.
Używanie zoptymalizowanych kontenerów danych
Niektóre klasy udostępniane przez język programowania nie są zoptymalizowane pod kątem używania na urządzeniach mobilnych. Na przykład ogólna
HashMap implementacja może być nieefektywna pod względem pamięci, ponieważ wymaga oddzielnego obiektu wpisu dla każdego mapowania.
Framework Androida zawiera kilka zoptymalizowanych kontenerów danych, w tym
SparseArray,
SparseBooleanArray i
LongSparseArray. Na przykład klasy SparseArray są bardziej wydajne, ponieważ unikają konieczności automatycznego opakowywania klucza i czasami wartości przez system, co tworzy jeszcze 1 lub 2 obiekty na wpis.
W razie potrzeby zawsze możesz przejść na surowe tablice, aby uzyskać uproszczoną strukturę danych.
Uważaj na abstrakcje kodu
Programiści często używają abstrakcji jako dobrej praktyki programowania, ponieważ mogą one zwiększyć elastyczność kodu i ułatwić jego konserwację. Abstrakcje zwykle wymagają jednak wykonania większej ilości kodu. Jak opisano w sekcji Zmniejszanie rozmiaru kodu i zasobów aplikacji, większa skompilowana baza kodu bezpośrednio zwiększa ilość fizycznej pamięci RAM wymaganej przez aplikację. Jeśli abstrakcje nie są znacząco korzystne, unikaj ich.
Używanie lite protobufs do serializowanych danych
Bufory protokołu (protobufs) to niezależny od języka, platformy i rozszerzalny mechanizm opracowany przez Google do serializowania uporządkowanych danych – podobny do XML, ale mniejszy, szybszy i prostszy. Jeśli używasz protobufs do danych, w kodzie po stronie klienta zawsze używaj lite protobufs. Zwykłe protobufs generują bardzo rozbudowany kod, co zwiększa rozmiar kodu aplikacji w pamięci RAM (patrz Zmniejszanie rozmiaru kodu i zasobów aplikacji) i przyczynia się do zwiększenia rozmiaru APK.
Więcej informacji znajdziesz w pliku README protobufs.
Uważaj na wycieki pamięci
Nieprawidłowe zarządzanie odwołaniami może prowadzić do wycieków pamięci, w których obiekty żyją dłużej niż jest to konieczne, co uniemożliwia odzyskanie pamięci wyciekającego obiektu przez odśmiecacz pamięci. Aby uniknąć wycieków pamięci, zaimplementuj projekt uwzględniający cykl życia.
Unikaj fragmentacji pamięci
Zdarzenia odśmiecania pamięci nie wpływają na wydajność aplikacji. Jednak wiele zdarzeń odśmiecania pamięci, które występują w krótkim czasie, może szybko wyczerpać baterię, a także nieznacznie zwiększyć czas konfiguracji klatek ze względu na konieczne interakcje między odśmiecaczem pamięci a wątkami aplikacji. Im więcej czasu system poświęca na odśmiecanie pamięci, tym szybciej wyczerpuje się bateria.
Często fragmentacja pamięci może powodować występowanie dużej liczby zdarzeń odśmiecania pamięci. W praktyce churn pamięci opisuje liczbę przydzielonych obiektów tymczasowych, które występują w danym okresie.
Na przykład możesz przydzielić wiele obiektów tymczasowych w pętli for.
Możesz też utworzyć nowe Paint lub
Bitmap obiekty w funkcji widoku.onDraw() W obu przypadkach aplikacja szybko tworzy dużą liczbę obiektów. Mogą one szybko zużyć całą dostępną pamięć w młodej generacji, co wymusza wystąpienie zdarzenia odśmiecania pamięci.
Użyj Memory Profiler, aby znaleźć miejsca w kodzie, w których fragmentacja pamięci jest wysoka, zanim rozwiążesz problemy.
Po zidentyfikowaniu problematycznych obszarów w kodzie spróbuj zmniejszyć liczbę przydziałów w obszarach krytycznych dla wydajności. Rozważ przeniesienie elementów poza pętle wewnętrzne lub przeniesienie ich do struktury przydzielania opartej na fabryce przydzielania struktury.
Możesz też sprawdzić, czy pule obiektów są korzystne w danym przypadku. W przypadku puli obiektów, zamiast usuwać instancję obiektu, zwalniasz ją do puli, gdy nie jest już potrzebna. Następnym razem, gdy potrzebna będzie instancja obiektu tego typu, możesz ją pobrać z puli zamiast przydzielać.
Dokładnie oceń wydajność, aby określić, czy pula obiektów jest odpowiednia w danej sytuacji. W niektórych przypadkach pule obiektów mogą pogorszyć wydajność. Chociaż pule unikają przydziałów, wprowadzają inne obciążenia. Na przykład utrzymywanie puli zwykle wymaga synchronizacji, która ma niepomijalne obciążenie. Czyszczenie instancji obiektu w puli, aby uniknąć wycieków pamięci podczas zwalniania, a następnie jego inicjowanie podczas pobierania może też mieć niezerowe obciążenie.
Przechowywanie w puli większej liczby instancji obiektów niż jest to konieczne również obciąża odśmiecacz pamięci. Chociaż pule obiektów zmniejszają liczbę wywołań odśmiecania pamięci, zwiększają ilość pracy potrzebnej do każdego wywołania, ponieważ jest ona proporcjonalna do liczby aktywnych (osiągalnych) bajtów.