Zarządzanie pamięcią aplikacji

Na tej stronie znajdziesz informacje o tym, jak aktywnie zmniejszać wykorzystanie pamięci w aplikacji. Więcej informacji o tym, jak system operacyjny Android zarządza pamięcią, znajdziesz w omówieniu zarządzania pamięcią.

Pamięć RAM jest cennym zasobem w każdym środowisku programistycznym, a jeszcze cenniejsza jest w przypadku mobilnego systemu operacyjnego, w którym pamięć fizyczna jest często ograniczona. Chociaż środowisko wykonawcze Androida (ART) i maszyna wirtualna Dalvik przeprowadzają rutynowe czyszczenie pamięci, nie oznacza to, że możesz ignorować, kiedy i gdzie Twoja aplikacja przydziela i zwalnia pamięć. Nadal musisz unikać wycieków pamięci, które zwykle są spowodowane 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 zewnętrznych i 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 rozbudowane komponenty, zasoby i biblioteki.

Zmniejszanie ogólnego rozmiaru aplikacji przez włączenie R8

Skompilowany kod aplikacji jest aktywną częścią pamięci środowiska wykonawczego. Każda klasa, metoda, zależność biblioteki i stała tekstowa musi być wczytywana do pamięci RAM podczas działania. Im większa jest skompilowana baza kodu, tym więcej pamięci RAM wymaga aplikacja.

Możesz użyć R8, aby zmniejszyć wykorzystanie pamięci przez aplikację. R8 jest tradycyjnie znany z zmniejszania rozmiaru pliku APK, ale ma też bezpośredni, pozytywny wpływ na pamięć RAM w czasie działania. R8 analizuje kod bajtowy aplikacji, aby usuwać martwy kod, scalać zbędne klasy, wstawiać metody i minimalizować identyfikatory. Dzięki wczytywaniu do pamięci RAM mniejszej ilości skompilowanego kodu bajtowego z pliku APK zmniejsza ogólne podstawowe wykorzystanie pamięci przez aplikację. Dodatkowo minimalizowanie nazw klas, metod i pól do krótszych identyfikatorów bezpośrednio zmniejsza narzut pamięci RAM. Optymalizacje takie jak łączenie klas i rozbudowane wstawianie metod zastępują też kosztowne wyszukiwania w czasie działania i wzorce alokacji, co skutkuje zoptymalizowaną pamięcią sterty i stosu.

Omówienie reguł przechowywania

Reguły zachowywania to instrukcje konfiguracyjne, które informują R8, które części kodu należy zachować podczas optymalizacji. Zapobiegają one usuwaniu lub minimalizowaniu kodu, na którym opiera się aplikacja. Więcej informacji znajdziesz w artykule Omówienie reguł przechowywania.

Źle napisane reguły keep uniemożliwiają R8 optymalizację dużych części bazy kodu. Unikaj zbyt ogólnych reguł przechowywania i postępuj zgodnie z tymi sprawdzonymi metodami:

  • Globalne reguły, których należy unikać:
    • -dontoptimize: całkowicie wyłącza optymalizację całej aplikacji, co powoduje, że pliki wykonywalne są większe i działają wolniej.
    • -dontshrink: zapobiega usuwaniu nieużywanego kodu i zasobów.
    • -dontobfuscate: zapobiega minifikacji nazw, co uniemożliwia uzyskanie znacznych oszczędności pamięci (zwłaszcza w przypadku dużych aplikacji).
  • Unikaj symboli wieloznacznych obejmujących cały pakiet: szerokie reguły, takie jak -keep class com.example.package.** { *; }, wymuszają zachowanie przez R8 każdej klasy, pola i metody w tym pakiecie. Całkowicie uniemożliwia to R8 usuwanie, optymalizowanie i minimalizowanie kodu w tym pakiecie.

  • Użyj domyślnego pliku konfiguracji R8: zawsze używaj proguard-android-optimize.txt.

Więcej informacji o tworzeniu reguł przechowywania znajdziesz w artykule Omówienie reguł przechowywania. Konkretne wzorce, których należy używać i których należy unikać, znajdziesz w artykule Sprawdzone metody dotyczące reguł Keep.

Analizator konfiguracji R8 dostarcza informacji 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 blokujące optymalizację, znajdziesz w artykule Analizator konfiguracji R8.

Uważaj na korzystanie z bibliotek zewnętrznych

Kod biblioteki zewnętrznej często nie jest pisany z myślą o środowiskach mobilnych i może być nieefektywny w pracy na urządzeniu mobilnym. Jeśli używasz biblioteki zewnętrznej, może być konieczne zoptymalizowanie jej pod kątem urządzeń mobilnych. Zaplanuj te działania z wyprzedzeniem i przeanalizuj bibliotekę pod kątem rozmiaru kodu i zajętości pamięci RAM przed jej użyciem.

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ć uproszczonych protokołów protobuf, a inna – mikroprotokołów protobuf, co spowoduje, że w aplikacji będą 2 różne implementacje protokołów protobuf. Może się to zdarzyć w przypadku różnych implementacji rejestrowania, analiz, platform wczytywania obrazów, buforowania i wielu innych rzeczy, których się nie spodziewasz.

Optymalizacja aplikacji za pomocą R8 może usuwać nieużywany kod z zależności, ale jej skuteczność jest często ograniczona przez wewnętrzną konfigurację biblioteki. Na przykład ogólne reguły zachowywania lub użycie odbicia w bibliotece mogą uniemożliwić R8 zmniejszenie kodu, co prowadzi do większego zużycia pamięci. Strategie wyboru wydajnych bibliotek znajdziesz w artykule Rozważny wybór bibliotek.

Unikaj używania biblioteki wspólnej tylko w przypadku 1–2 funkcji z kilkudziesięciu. Nie pobieraj dużej ilości kodu i dodatkowych elementów, których nie używasz. Zastanawiając się, czy użyć biblioteki, poszukaj implementacji, która w dużym stopniu 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 przez Ciebie kod i zapewnić adaptacyjne środowisko przydatne do testowania i innych zmian konfiguracji.

Jeśli zamierzasz używać w aplikacji platformy wstrzykiwania zależności, rozważ użycie Hilt lub Daggera. Hilt to biblioteka wstrzykiwania zależności na Androida, która działa na platformie Dagger. Dagger nie używa refleksji do skanowania kodu aplikacji. Możesz używać statycznej implementacji Daggera w czasie kompilacji w aplikacjach na Androida bez niepotrzebnych kosztów w czasie działania i wykorzystania pamięci.

Inne platformy wstrzykiwania zależności, które korzystają z odbicia, 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 zachowaj ostrożność, aby uniknąć wycieków pamięci. W tym celu dopilnuj, aby obiekty miały odpowiedni zakres. 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. Więcej informacji znajdziesz w wskazówkach dotyczących unikania wycieków pamięci w przypadku obiektów o określonym zakresie.

Świadomie wczytuj obrazy

Bitmapy graficzne są zwykle największymi obiektami w pamięci aplikacji. Nawet jeśli pracujesz ze skompresowanymi plikami, takimi jak JPEG, plik musi zostać rozpakowany do nieskompresowanej mapy bitowej, aby można go było wyświetlić na ekranie. Mały skompresowany plik obrazu może zostać przekształcony w bardzo dużą bitmapę.

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 kanał alfa (przezroczystość). Jeśli masz plik JPEG o rozmiarze 100 KB i wyświetlasz go w widoku o rozmiarze 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ć wykorzystanie obrazów, możesz zrobić kilka rzeczy. Na przykład biblioteki wczytywania obrazów mogą pomóc w zwalnianiu pamięci, gdy nie jest ona potrzebna. Informacje o tym, jak efektywnie obsługiwać obrazy, znajdziesz w artykule Optymalizacja obrazów bitmapowych.

Monitorowanie dostępnej pamięci i wykorzystania pamięci

Aby rozwiązać problemy z wykorzystaniem pamięci przez aplikację, musisz je najpierw znaleźć. Profiler pamięci w Android Studio pomaga znajdować i diagnozować problemy z pamięcią na te sposoby:

Profiler pamięci jest też zintegrowany z biblioteką LeakCanary do wykrywania wycieków pamięci. 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 Androida Studio.

Istnieją inne narzędzia, których możesz użyć do diagnozowania problemów z pamięcią na podstawie danych od użytkowników korzystających z Twojej aplikacji w wersji produkcyjnej:

Zwalnianie pamięci w reakcji na zdarzenia

Android może odzyskać pamięć z aplikacji lub całkowicie ją zatrzymać, jeśli jest to konieczne, aby zwolnić pamięć na potrzeby krytycznych zadań, co zostało wyjaśnione w omówieniu zarządzania pamięcią. Aby jeszcze bardziej zrównoważyć pamięć systemową i uniknąć konieczności zatrzymania procesu aplikacji przez system, możesz zaimplementować interfejs ComponentCallbacks2 w klasach Activity. Podana metoda wywołania zwrotnego onTrimMemory() 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ę. Zwolnienie 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_HIDDENTRIM_MEMORY_BACKGROUND. (Od Androida 14 system nie dostarcza już powiadomień dotyczących innych, starszych stałych). Te stałe zostały formalnie wycofane w Androidzie 15).

  • TRIM_MEMORY_UI_HIDDEN: ten sygnał wskazuje, że interfejs aplikacji zniknął z widoku użytkownika. Ta zmiana daje możliwość zwolnienia znacznych ilości alokacji pamięci przydzielonej wyłącznie interfejsowi, np. bitmap, buforów odtwarzania wideo czy złożonych zasobów animacji.

  • TRIM_MEMORY_BACKGROUND: ten sygnał oznacza, że proces działa w tle i może zostać zakończony, 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ć po wznowieniu sesji przez użytkownika.

Ten przykładowy kod pokazuje, jak wdrożyć wywołanie zwrotne onTrimMemory(), aby reagować 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 sztywny limit rozmiaru sterty przydzielonej każdej aplikacji. Dokładny limit rozmiaru sterty różni się w zależności od urządzenia i zależy od ilości pamięci RAM dostępnej na urządzeniu. Jeśli aplikacja osiągnie pojemność sterty i spróbuje przydzielić więcej pamięci, system zgłosi błąd OutOfMemoryError.

Aby uniknąć wyczerpania pamięci, możesz wysłać do systemu zapytanie o ilość dostępnego miejsca na stercie na bieżącym urządzeniu. Możesz wysłać do systemu zapytanie o tę wartość, wywołując funkcję getMemoryInfo(). Zwraca 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, czyli prostą wartość logiczną, która informuje, 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 zamknięć z powodu małej ilości pamięci

Błędy LMK (low memory kill) widoczne dla użytkowników występują, gdy pamięć systemowa jest krytycznie mała. Gdy pamięć jest prawie pełna, lmkd (demon LMK) zamyka procesy na podstawie ich 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 w pierwszej kolejności. Jeśli pamięć nadal jest krytycznie mała, demon jest zmuszony do odzyskania pamięci z procesów o wartości oom_adj_score równej 0. Ten wynik jest zarezerwowany dla widocznych aplikacji, więc ich zakończenie powoduje natychmiastowe, nieprawidłowe zamknięcie procesu. Dla użytkownika końcowego wygląda to tak, jakby aplikacja uległa awarii, często z pominięciem standardowych mechanizmów zapisywania stanu cyklu życia i utratą postępów użytkownika.

Zakończenia procesów na pierwszym planie są głównym elementem w Android Vitals, ponieważ stanowią dokładny wskaźnik nieprawidłowego zarządzania pamięcią. Wskaźnik LMK powyżej 1% wskazuje na pilną potrzebę podjęcia natychmiastowych działań, ale niski wskaźnik nie musi oznaczać, że wszystko jest w porządku. Niska częstotliwość błędów LMK widocznych dla użytkowników może oznaczać, że demon LMK często zamyka procesy działające w tle, co pogarsza wydajność uruchamiania częściowo z pamięci i płynność wielozadaniowości. Dlatego zalecamy stosowanie sprawdzonych metod zarządzania pamięcią niezależnie od aktualnego wyniku LMK, aby zapewnić długoterminową stabilność i dobry stan urządzenia.

Śledzenie problemów z pamięcią za pomocą aplikacji ProfilingManager

Platforma Android udostępnia ProfilingManager, zaawansowany interfejs API do obserwacji, który umożliwia rejestrowanie danych użytkowników w środowisku produkcyjnym na podstawie ustawionych przez Ciebie wyzwalaczy. Może to pomóc w wykryciu 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_OOM oznacza, że aplikacja zgłosiła OutOfMemoryError. Zostanie on wywołany przy następnym uruchomieniu aplikacji po awarii, gdy zarejestruje ona wyzwalacze profilowania.
  • TRIGGER_TYPE_ANOMALY jest wywoływany, gdy system wykryje nietypowe zachowanie aplikacji. Może to być spowodowane m.in. nadmiernym wykorzystaniem pamięci. Jest on wywoływany, gdy aplikacja wykazuje nadmierne wykorzystanie pamięci, ale zanim system podejmie jakiekolwiek działania w celu zatrzymania procesu powodującego problem. Jeśli na przykład aplikacja przekracza limity pamięci wprowadzone w Androidzie 17,TRIGGER_TYPE_ANOMALY wywołuje zdarzenia przed zamknięciem aplikacji przez system.

Więcej informacji o używaniu ProfilingManager do programowego rejestrowania i pobierania wyzwalaczy znajdziesz w dokumentacji profilowania opartego na wyzwalaczach.

Możesz też użyć profilowania opartego na aplikacji, aby ręcznie zdefiniować punkty początkowe i końcowe śledzenia. Zalecamy to rozwiązanie, jeśli chcesz ręcznie przechwytywać zrzuty sterty lub profile sterty w obszarach, w których podejrzewasz wycieki pamięci lub nadmierne wykorzystanie pamięci.

Używaj bardziej efektywnych pod względem pamięci 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 korzystanie z usług

Stanowczo odradzamy pozostawianie usług włączonych, gdy nie jest to konieczne. Pozostawianie uruchomionych niepotrzebnych usług to jeden 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 wykonać zadanie. Zatrzymaj usługę po zakończeniu zadania. W przeciwnym razie może dojść do wycieku pamięci.

Gdy uruchomisz usługę, system będzie starał się utrzymać proces tej usługi. Takie 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ę procesów w pamięci podręcznej, które system może przechowywać w pamięci podręcznej LRU, co obniża wydajność przełączania aplikacji. Może to nawet prowadzić do niestabilności systemu, gdy pamięci jest za mało i nie można utrzymać wystarczającej liczby procesów do obsługi wszystkich aktualnie działających usług.

Zasadniczo unikaj korzystania z usług trwałych 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 ciągł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 implementacja HashMap może być nieefektywna pod względem pamięci, ponieważ wymaga oddzielnego obiektu wpisu dla każdego mapowania.

Platforma Androida zawiera kilka zoptymalizowanych kontenerów danych, w tym SparseArray, SparseBooleanArrayLongSparseArray. Na przykład klasy SparseArray są bardziej wydajne, ponieważ nie wymagają automatycznego opakowywania klucza, a czasami także wartości, co powoduje utworzenie kolejnych obiektów dla każdego wpisu.

W razie potrzeby możesz zawsze przełączyć się na surowe tablice, aby uzyskać prostą 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 artykule Zmniejszanie rozmiaru kodu i zasobów aplikacji, większa skompilowana baza kodu bezpośrednio zwiększa ilość pamięci RAM, której wymaga aplikacja. Jeśli abstrakcje nie przynoszą znaczących korzyści, unikaj ich.

Używanie uproszczonych buforów protokołu do serializacji danych

Bufory protokołu to niezależny od języka i platformy rozszerzalny mechanizm zaprojektowany przez Google do serializowania danych strukturalnych – podobny do XML, ale mniejszy, szybszy i prostszy. Jeśli do przechowywania danych używasz protokołów buforowanych, w kodzie po stronie klienta zawsze używaj protokołów buforowanych w wersji lite. Zwykłe bufory protokołu generują bardzo obszerny kod, co zwiększa rozmiar kodu aplikacji w pamięci RAM (patrz Zarządzanie rozmiarem kodu aplikacji i jego optymalizacja) i przyczynia się do zwiększenia rozmiaru pliku APK.

Więcej informacji znajdziesz w pliku readme protobuf.

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ż powinny, co uniemożliwia odzyskanie pamięci wyciekłego obiektu przez moduł odśmiecania. Aby uniknąć wycieków pamięci, zastosuj projektowanie z uwzględnieniem cyklu życia.

Więcej informacji znajdziesz w sekcji Wycieki pamięci.

Zapobieganie fragmentacji pamięci

Zdarzenia czyszczenia pamięci nie mają wpływu na wydajność aplikacji. Jednak wiele zdarzeń czyszczenia pamięci, które występują w krótkim czasie, może szybko wyczerpać baterię, a także nieznacznie wydłużyć czas konfiguracji klatek ze względu na niezbędne interakcje między czyszczeniem pamięci a wątkami aplikacji. Im więcej czasu system poświęca na czyszczenie pamięci, tym szybciej wyczerpuje się bateria.

Często intensywne wykorzystanie pamięci może powodować dużą liczbę zdarzeń odśmiecania. W praktyce churn pamięci opisuje liczbę tymczasowych obiektów przydzielonych w określonym czasie.

Możesz na przykład przydzielić wiele obiektów tymczasowych w pętli for. Możesz też utworzyć nowe obiekty Paint lub Bitmap w funkcji onDraw() widoku. W obu przypadkach aplikacja szybko tworzy dużą liczbę obiektów. Mogą one szybko zużyć całą dostępną pamięć w młodszej generacji, co wymusza uruchomienie czyszczenia pamięci.

Użyj profilera pamięci, aby znaleźć w kodzie miejsca, w których występuje wysokie zużycie pamięci, zanim je naprawisz.

Po zidentyfikowaniu problematycznych obszarów w kodzie spróbuj zmniejszyć liczbę alokacji w obszarach o krytycznym znaczeniu dla wydajności. Rozważ przeniesienie elementów poza pętle wewnętrzne lub do struktury alokacji opartej na fabryce.

Możesz też ocenić, czy pule obiektów są korzystne w danym przypadku użycia. W przypadku puli obiektów zamiast upuszczać instancję obiektu na podłogę, zwalniasz ją do puli, gdy nie jest już potrzebna. Następnym razem, gdy będzie potrzebna 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ść. Pule unikają przydzielania, ale wprowadzają inne obciążenia. Na przykład utrzymywanie puli zwykle wiąże się z synchronizacją, która generuje znaczne obciążenie. Wyczyszczenie instancji obiektu z puli, aby uniknąć wycieków pamięci podczas zwalniania, a następnie zainicjowanie jej podczas pozyskiwania może wiązać się z pewnym narzutem.

Przechowywanie w puli większej liczby instancji obiektów niż jest to potrzebne również obciąża mechanizm czyszczenia pamięci. Pule obiektów zmniejszają liczbę wywołań odzyskiwania pamięci, ale zwiększają ilość pracy potrzebnej do każdego wywołania, ponieważ jest ona proporcjonalna do liczby aktywnych (osiągalnych) bajtów.