Zrób zrzut stosu

Zrób zrzut sterty, aby sprawdzić, które obiekty w aplikacji zużywają pamięć w momencie zrzutu, i zidentyfikować wycieki pamięci lub zachowanie związane z alokacją pamięci które prowadzi do zacinania się, zawieszania, a nawet awarii aplikacji. Szczególnie przydatne jest robienie zrzutów sterty po dłuższej sesji użytkownika, gdy może to pokazać obiekty, które nadal znajdują się w pamięci, a nie powinny już tam być.

Na tej stronie opisujemy narzędzia, które Android Studio udostępnia do zbierania i analizowania zrzutów sterty. Możesz też sprawdzić pamięć aplikacji z wiersza poleceń za pomocą dumpsys oraz zobaczyć zdarzenia związane z czyszczeniem pamięci (GC) w Logcat.

Dlaczego warto profilować pamięć aplikacji

Android provides a managed memory environment—when Android determines that your app is no longer using some objects, the garbage collector releases the unused memory back to the heap. Sposób, w jaki Android znajduje nieużywaną pamięć, jest stale ulepszany, ale w pewnym momencie we wszystkich wersjach Androida system musi na chwilę wstrzymać Twój kod. W większości przypadków przerwy są niezauważalne. Jeśli jednak aplikacja przydziela pamięć szybciej niż system może ją zbierać, aplikacja może się opóźniać, gdy odśmiecacz zwalnia wystarczającą ilość pamięci, aby zaspokoić alokacje. Opóźnienie może spowodować, że aplikacja będzie pomijać klatki i widocznie spowolnić działanie.

Nawet jeśli aplikacja nie wykazuje spowolnienia, może ona wyciekać pamięć i zachowywać ją nawet wtedy, gdy działa w tle. Takie zachowanie może spowolnić działanie pamięci w pozostałej części systemu, wymuszając niepotrzebne zdarzenia czyszczenia pamięci. W końcu system jest zmuszony do zamknięcia procesu aplikacji, aby odzyskać pamięć. Gdy użytkownik wróci do aplikacji, proces aplikacji musi zostać całkowicie uruchomiony ponownie.

Więcej informacji o praktykach programistycznych, które mogą zmniejszyć zużycie pamięci przez aplikację , znajdziesz w artykule Zarządzanie pamięcią aplikacji.

Omówienie zrzutu sterty

Aby zrobić zrzut sterty, wybierz zadanie Analizuj zużycie pamięci (zrzut sterty) (użyj Profiler: run 'app' as debuggable (complete data)), aby zrobić zrzut sterty. Podczas zrzutu sterty ilość pamięci Javy może tymczasowo wzrosnąć. Jest to normalne, ponieważ zrzut sterty odbywa się w tym samym procesie co aplikacja i wymaga pewnej ilości pamięci do zebrania danych. Po zrobieniu zrzutu sterty zobaczysz te informacje:

Widok zrzutu sterty w Profilerze Android Studio.

Lista klas zawiera te informacje:

  • Allocations (Alokacje): liczba alokacji w stercie.
  • Native Size (Rozmiar natywny): łączna ilość pamięci natywnej używanej przez ten typ obiektu (w bajtach). W przypadku niektórych obiektów przydzielonych w Javie zobaczysz tutaj pamięć, ponieważ Android używa pamięci natywnej w przypadku niektórych klas frameworka, takich jak Bitmap.

  • Shallow Size (Rozmiar płytki): łączna ilość pamięci Javy używanej przez ten typ obiektu (w bajtach).

  • Rozmiar zachowany: łączny rozmiar pamięci zachowanej ze względu na wszystkie instancje tej klasy (w bajtach).

Użyj menu sterty, aby odfiltrować określone sterty:

  • Stos aplikacji (domyślnie): główny stos, na którym aplikacja przydziela pamięć.
  • Image heap (Stos obrazu): obraz rozruchowy systemu zawierający klasy, które są wstępnie ładowane podczas uruchamiania. Alokacje w tym miejscu nigdy się nie przenoszą ani nie znikają.
  • Zygote heap (Stos Zygote): stos kopiowania przy zapisie, z którego w systemie Android rozwidla się proces aplikacji.

Użyj menu Arrangement (Ułożenie), aby wybrać sposób rozmieszczenia alokacji:

  • Ułóż według klasy (domyślnie): grupuje wszystkie alokacje na podstawie nazwy klasy.
  • Arrange by package (Ułóż według pakietu): grupuje wszystkie alokacje na podstawie nazwy pakietu.

Użyj menu Class (Klasa), aby odfiltrować grupy klas:

  • All classes (default) (Wszystkie klasy (domyślnie)): pokazuje wszystkie klasy, w tym klasy z bibliotek i zależności.
  • Show activity/fragment leaks (Pokaż wycieki aktywności/fragmentów): pokazuje klasy, które powodują wycieki pamięci.
  • Show project classes (Pokaż klasy projektu): pokazuje tylko klasy zdefiniowane przez Twój projekt.

Kliknij nazwę klasy, aby otworzyć panel Instance (Instancja). Każda instancja na liście zawiera te informacje:

  • Depth (Głębokość): najkrótsza liczba przeskoków od dowolnego korzenia GC do wybranej instancji.
  • Native Size (Rozmiar natywny): rozmiar tej instancji w pamięci natywnej. Ta kolumna jest widoczna tylko w Androidzie 7.0 i nowszym.
  • Shallow Size (Rozmiar płytki): rozmiar tej instancji w pamięci Javy.
  • Retained Size (Rozmiar zachowany): rozmiar pamięci, którą ta instancja dominuje (zgodnie z drzewem dominatorów).

Kliknij instancję, aby wyświetlić Instance Details (Szczegóły instancji), w tym Fields (Pola) i References (Odwołania). Typowe typy pól i odwołań to typy strukturalne , tablice , i podstawowe typy danych w Javie. Kliknij pole lub odwołanie prawym przyciskiem myszy, aby przejść do powiązanej instancji lub wiersza w kodzie źródłowym.

  • Fields (Pola): pokazuje wszystkie pola w tej instancji.
  • References (Odwołania): pokazuje wszystkie odwołania do obiektu wyróżnionego na karcie Instance (Instancja).
Widoki Instances (Instancje), Fields (Pola) i References (Odwołania) w oknie narzędzia Zrzut sterty.

Znajdowanie wycieków pamięci

Aby szybko odfiltrować klasy, które mogą być powiązane z wyciekami pamięci, otwórz menu Class (Klasa) i wybierz Show activity/fragment leaks (Pokaż wycieki aktywności/fragmentów). Android Studio pokazuje klasy, które według niego wskazują na wycieki pamięci w przypadku instancji Activity i Fragment w Twojej aplikacji.

Aby ręcznie wyszukać wycieki pamięci, przejrzyj listy klas i instancji, aby znaleźć obiekty o dużym Retained Size (Rozmiarze zachowanym). Poszukaj wycieków pamięci spowodowanych przez jedną z tych przyczyn:

  • Długotrwałe odwołania do Activity lub Context, które mogą powodować wyciek hostowanego wykresu kompozycji Compose (np. ComposeView i jego podkompozycje).
  • Wyciekające obiekty stanu Jetpack Compose (MutableState), kontenery stanu lub lambdy, które przechwytują Context.
  • Zapominanie o zwalnianiu miejsca dla słuchaczy lub obserwatorów w bloku onDispose elementu DisposableEffect.
  • Niestatyczne klasy wewnętrzne, takie jak a Runnable, które mogą zawierać instancję Activity.
  • Pamięci podręczne, które przechowują obiekty dłużej niż jest to konieczne.

Gdy znajdziesz potencjalne wycieki pamięci, użyj kart Fields (Pola) i References (Odwołania) w sekcji Instance Details (Szczegóły instancji), aby przejść do interesującej Cię instancji lub wiersza kodu źródłowego.

Wywoływanie wycieków pamięci na potrzeby testowania

Aby analizować wykorzystanie pamięci, musisz obciążyć kod aplikacji i spróbować wymusić wycieki pamięci. Jednym ze sposobów na wywołanie wycieków pamięci w aplikacji jest uruchomienie jej na jakiś czas przed sprawdzeniem sterty. Wycieki mogą sięgać do górnej części alokacji w stercie. Im mniejszy wyciek, tym dłużej musisz uruchamiać aplikację, aby go zobaczyć.

Wyciek pamięci możesz też wywołać na jeden z tych sposobów:

  • Wielokrotnie obracaj urządzenie z orientacji pionowej do poziomej i z powrotem w różnych stanach aktywności. Obracanie urządzenia może często powodować wyciek Activity (a w konsekwencji hostowanego drzewa interfejsu Compose i powiązanych drzew stanów), jeśli aplikacja zawiera odwołanie do Activity lub Context w operacjach asynchronicznych lub kontenerach stanu.
  • Przełączaj się między aplikacją a inną aplikacją w różnych stanach aktywności. Na przykład przejdź do ekranu głównego, a potem wróć do aplikacji.

Eksportowanie i importowanie nagrania zrzutu sterty

Plik zrzutu sterty możesz eksportować i importować z karty Past Recordings (Poprzednie nagrania) w profilerze. Android Studio zapisuje nagranie jako plik .hprof.

Jeśli chcesz użyć innego analizatora plików .hprof, np. jhat, musisz przekonwertować plik .hprof z formatu Androida na format pliku .hprof Java SE. Aby przekonwertować format pliku, użyj narzędzia hprof-conv znajdującego się w katalogu {android_sdk}/platform-tools/. Uruchom polecenie hprof-conv z 2 argumentami: oryginalną nazwą pliku .hprof i lokalizacją, w której ma zostać zapisany przekonwertowany plik .hprof, w tym nową nazwą pliku .hprof. Przykład:

hprof-conv heap-original.hprof heap-converted.hprof

Dodatkowe materiały