Zarządzanie pamięcią aplikacji

Na tej stronie znajdziesz informacje o tym, jak aktywnie zmniejszać zużycie 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 w przypadku mobilnego systemu operacyjnego, w którym pamięć fizyczna jest często ograniczona, jest jeszcze cenniejsza. Chociaż środowisko wykonawcze Androida (ART) i maszyna wirtualna Dalvik przeprowadzają rutynowe czyszczenie pamięci, nie oznacza to, że możesz ignorować, kiedy i gdzie 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ć załadowana 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 ze zmniejszania rozmiaru pliku APK, ale ma też bezpośredni, pozytywny wpływ na pamięć środowiska wykonawczego (RAM). R8 analizuje kod bajtowy aplikacji, aby usuwać martwy kod, scalać nadmiarowe 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ą 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, aby zapobiec usuwaniu lub minimalizowaniu kodu, na którym opiera się aplikacja. Więcej informacji znajdziesz w artykule Omówienie reguł przechowywania.

Źle napisane reguły zachowywania uniemożliwiają R8 optymalizację dużych części bazy kodu. Unikaj zbyt ogólnych reguł zachowywania i stosuj te sprawdzone metody:

  • 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: ogólne reguły, takie jak -keep class com.example.package.** { *; }, zmuszają R8 do zachowywania każdej klasy, pola i metody w tym pakiecie. Całkowicie uniemożliwia to R8 usuwanie, optymalizowanie lub 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 omówieniu reguł przechowywania. Konkretne wzorce, których należy używać i których należy unikać, znajdziesz w artykule Sprawdzone metody dotyczące reguł przechowywania.

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 sekcji 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 z powodu różnych implementacji. Na przykład jedna biblioteka może używać uproszczonych buforów protokołu, a inna – mikrobuforów protokołu, co spowoduje, że w aplikacji będą 2 różne implementacje buforów protokołu. Może się to zdarzyć w przypadku różnych implementacji rejestrowania, analiz, platform wczytywania obrazów, pamięci podręcznej i wielu innych elementów, 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 refleksji 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 odbicia 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 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 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 przewodniku na temat 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 było go 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.

Istnieje kilka sposobów optymalizacji wykorzystania obrazów. Na przykład używanie bibliotek wczytywania obrazów może pomóc w zwalnianiu pamięci, gdy nie jest ona potrzebna. Więcej informacji o wydajnym zarządzaniu obrazami znajdziesz w artykule Optymalizacja obrazów bitmapowych.

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:

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 aplikacji w wersji produkcyjnej:

Zwalnianie pamięci w reakcji na zdarzenia

W razie potrzeby Android może odzyskać pamięć z aplikacji lub całkowicie ją zatrzymać, aby zwolnić pamięć na potrzeby zadań krytycznych, co zostało wyjaśnione w artykule Omówienie 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. Udostępniona 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 zużycia pamięci przez aplikację. Zwolnienie pamięci może zmniejszyć częstotliwość zamykania aplikacji przez proces zamykania aplikacji z 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 został usunięty z widoku użytkownika. Ta zmiana daje możliwość zwolnienia znacznych ilości pamięci przydzielonej wyłącznie do interfejsu, np. map bitowych, buforów odtwarzania wideo lub 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ępnej pamięci sterty na bieżącym urządzeniu. Aby uzyskać tę wartość, wywołaj funkcję 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ż funkcję lowMemory, która jest prostą wartością logiczną informującą, czy na urządzeniu kończy się pamięć.

Ten 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 małej ilości pamięci

Błędy LMK widoczne dla użytkowników występują, gdy pamięć systemowa jest krytycznie mała. Gdy pamięć jest mała, lmkd (demon LMK) kończy procesy na podstawie ich oom_adj_score. Aplikacje, które są w pamięci podręcznej lub uruchamiają usługę bez powiązanego interfejsu (np. zadanie), mają najwyższe wyniki i są zamykane jako pierwsze. Jeśli pamięć nadal jest krytycznie mała, demon jest zmuszony do odzyskania pamięci z procesów o wartości oom_adj_score 0. Ponieważ ten wynik jest zarezerwowany dla widocznych aplikacji, ich zamknięcie powoduje natychmiastowe, nieprawidłowe zakończenie procesu. Dla użytkownika 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.

Zamykanie procesów na pierwszym planie jest głównym problemem w Android Vitals, ponieważ stanowi wiarygodny wskaźnik nieprawidłowego zarządzania pamięcią. Chociaż każdy wskaźnik LMK powyżej 1% wskazuje na pilną potrzebę podjęcia natychmiastowych działań, niski wskaźnik niekoniecznie 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ść „ciepłego startu” i płynność wielozadaniowości. Dlatego zalecamy przestrzeganie sprawdzonych metod zarządzania pamięcią niezależnie od aktualnego wyniku LMK, aby zapewnić długoterminową stabilność i prawidłowe działanie urządzenia.

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

Platforma Androida udostępniaProfilingManager 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 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_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. Na przykład jeśli aplikacja przekracza limity pamięci wprowadzone w Androidzie 17,TRIGGER_TYPE_ANOMALY wywołuje się 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żywać profilowania opartego na aplikacji, aby ręcznie definiować 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 wydajnych 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

Zdecydowanie zalecamy, aby nie pozostawiać usług uruchomionych, gdy nie jest to konieczne. Pozostawianie niepotrzebnych usług uruchomionych 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 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ć jej proces w stanie aktywnym. 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 nadmiernego obciążenia 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 osobnego 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. Jest podobny do XML, ale mniejszy, szybszy i prostszy. Jeśli do przechowywania danych używasz protokołów buforów, w kodzie po stronie klienta zawsze używaj protokołów buforów 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 przez moduł odśmiecania. Aby uniknąć wycieków pamięci, zastosuj projektowanie uwzględniające cykl życia.

Unikaj fragmentacji pamięci

Zdarzenia czyszczenia pamięci nie mają wpływu na wydajność aplikacji. Jednak wiele zdarzeń odśmiecania, 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 modułem odśmiecania 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ć dużą liczbę zdarzeń czyszczenia pamięci. W praktyce churn pamięci opisuje liczbę przydzielonych obiektów tymczasowych, które występują w danym okresie.

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 duża rotacja 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. 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ść. Chociaż pule unikają alokacji, 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 odśmiecanie pamięci. Pule obiektów zmniejszają liczbę wywołań odśmiecania pamięci, ale zwiększają ilość pracy potrzebnej do każdego wywołania, ponieważ jest ona proporcjonalna do liczby aktywnych (osiągalnych) bajtów.