W tym dokumencie zakładamy, że stosujesz sprawdzone metody dotyczące aplikacji na Androida w zakresie zarządzania pamięcią, takie jak Zarządzanie pamięcią aplikacji.
Wprowadzenie
Wyciek pamięci to rodzaj wycieku zasobów, który występuje, gdy program komputerowy nie zwalnia przydzielonej pamięci, która nie jest już potrzebna. Wyciek może spowodować, że aplikacja zażąda od systemu operacyjnego więcej pamięci, niż jest dostępne, co doprowadzi do jej awarii. Wycieki pamięci w aplikacjach na Androida mogą być spowodowane przez różne nieprawidłowe praktyki, takie jak nieprawidłowe usuwanie zasobów lub nieodrejestrowywanie detektorów, gdy nie są już potrzebne.
W tym dokumencie znajdziesz sprawdzone metody, które pomogą Ci zapobiegać wyciekom pamięci w kodzie, wykrywać je i rozwiązywać. Jeśli po wypróbowaniu metod opisanych w tym dokumencie podejrzewasz, że w naszych pakietach SDK występuje wyciek pamięci, zapoznaj się z artykułem Jak zgłaszać problemy z pakietami SDK Google.
Zanim skontaktujesz się z zespołem pomocy
Zanim zgłosisz wyciek pamięci zespołowi pomocy Google, postępuj zgodnie ze sprawdzonymi metodami i wykonaj czynności debugowania opisane w tym dokumencie, aby upewnić się, że błąd nie występuje w Twoim kodzie. Te kroki mogą rozwiązać problem, a jeśli nie, wygenerują informacje, których zespół pomocy Google potrzebuje, aby Ci pomóc.
Zapobieganie wyciekom pamięci
Postępuj zgodnie z tymi sprawdzonymi metodami, aby uniknąć niektórych z najczęstszych przyczyn wycieków pamięci w kodzie, który korzysta z pakietów SDK Google.
Sprawdzone metody dotyczące aplikacji na Androida
Sprawdź, czy w aplikacji na Androida zostały wykonane wszystkie te czynności:
- Zwolnij nieużywane zasoby.
- Wyrejestrowywanie detektorów, gdy nie są już potrzebne
- Anuluj zadania, gdy nie są potrzebne.
- Przekazywanie metod cyklu życia w celu zwolnienia zasobów
- Używaj najnowszych wersji pakietów SDK.
- Unikaj blokowania wątku głównego podczas inicjowania, aby zapobiegać błędom ANR.
Szczegółowe informacje o każdej z tych praktyk znajdziesz w sekcjach poniżej.
Zwalnianie nieużywanych zasobów
Gdy aplikacja na Androida używa zasobu, pamiętaj, aby zwolnić go, gdy nie jest już potrzebny. Jeśli tego nie zrobisz, zasób będzie nadal zajmować pamięć, nawet po zakończeniu działania aplikacji. Więcej informacji znajdziesz w sekcji Cykl życia działania w dokumentacji Androida.
Zwolnienie nieaktualnych odwołań do GoogleMap w pakietach GeoSDK
Częstym błędem jest to, że obiekt GoogleMap może powodować wyciek pamięci, jeśli jest przechowywany w pamięci podręcznej za pomocą NavigationView lub MapView. Obiekt GoogleMap ma relację 1 do 1 z obiektem NavigationView lub MapView, z którego został pobrany. Musisz zadbać o to, aby obiekt GoogleMap nie był przechowywany w pamięci podręcznej lub aby odwołanie do niego było zwalniane po wywołaniu NavigationView#onDestroy lub MapView#onDestroy. Jeśli używasz NavigationSupportFragment, MapSupportFragment lub własnego fragmentu, który zawiera te widoki, odwołanie musi zostać zwolnione w Fragment#onDestroyView.
class NavFragment : SupportNavigationFragment() {
var googleMap: GoogleMap?
override fun onCreateView(
inflater: LayoutInflater,
parent: ViewGroup?,
savedInstanceState: Bundle?,
): View {
super.onCreateView(inflater,parent,savedInstanceState)
getMapAsync{map -> googleMap = map}
}
override fun onDestroyView() {
googleMap = null
}
}
Wyrejestrowywanie detektorów, gdy nie są już potrzebne
Gdy aplikacja na Androida rejestruje detektor zdarzenia, takiego jak kliknięcie przycisku lub zmiana stanu widoku, pamiętaj, aby wyrejestrować detektor, gdy aplikacja nie musi już monitorować zdarzenia. Jeśli tego nie zrobisz, detektory będą nadal zajmować pamięć, nawet gdy aplikacja przestanie ich używać.
Załóżmy na przykład, że Twoja aplikacja używa pakietu Navigation SDK i wywołuje ten detektor, aby nasłuchiwać zdarzeń przybycia:
addArrivalListener
metoda nasłuchiwania zdarzeń przybycia powinna też wywoływać
removeArrivalListener
gdy nie musi już monitorować zdarzeń przybycia.
var arrivalListener: Navigator.ArrivalListener? = null
fun registerNavigationListeners() {
arrivalListener =
Navigator.ArrivalListener {
...
}
navigator.addArrivalListener(arrivalListener)
}
override fun onDestroy() {
navView.onDestroy()
if (arrivalListener != null) {
navigator.removeArrivalListener(arrivalListener)
}
...
super.onDestroy()
}
Anulowanie zadań, gdy nie są potrzebne
Gdy aplikacja na Androida rozpoczyna zadanie asynchroniczne, takie jak pobieranie lub żądanie sieciowe, pamiętaj, aby anulować zadanie po jego zakończeniu. Jeśli zadanie nie zostanie anulowane, będzie nadal działać w tle, nawet po zakończeniu działania aplikacji.
Więcej informacji o sprawdzonych metodach znajdziesz w artykule Zarządzanie pamięcią aplikacji w dokumentacji Androida.
Przekazywanie metod cyklu życia w celu zwolnienia zasobów
Jeśli Twoja aplikacja używa pakietu Navigation SDK lub Maps SDK, pamiętaj o zwolnieniu zasobów przez przekazanie metod cyklu życia (pogrubionych) do navView. Możesz to zrobić za pomocą NavigationView w pakiecie Navigation SDK lub MapView w pakiecie Maps SDK lub Navigation SDK. Możesz też użyć SupportNavigationFragment lub SupportMapFragment zamiast bezpośredniego użycia odpowiednio NavigationView i MapView. Fragmenty obsługi zajmują się przekazywaniem metod cyklu życia.
class NavViewActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
navView = ...
navView.onCreate(savedInstanceState)
...
}
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
navView.onSaveInstanceState(savedInstanceState)
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
navView.onTrimMemory(level)
}
/* Same with
override fun onStart()
override fun onResume()
override fun onPause()
override fun onConfigurationChanged(...)
override fun onStop()
override fun onDestroy()
*/
}
Używaj najnowszych wersji pakietów SDK
Pakiety SDK Google są stale aktualizowane i uzupełniane o nowe funkcje, poprawki błędów i ulepszenia wydajności. Aby otrzymywać te poprawki, aktualizuj pakiety SDK w aplikacji.
Unikanie blokowania wątku głównego podczas inicjowania w celu zapobiegania błędom ANR
Gdy aplikacja zbyt długo blokuje główny wątek, może wystąpić błąd „Aplikacja nie odpowiada” (ANR). Aby zapobiegać błędom ANR, metody cyklu życia, takie jak onCreate(), powinny być jak najmniej obciążające. Możesz to osiągnąć, odraczając długotrwałe zadania lub uruchamiając je poza wątkiem głównym.
Aby uniknąć błędów ANR związanych z inicjowaniem pakietu SDK:
- Twórz tylko 1 instancję mapy naraz.
- Podczas tworzenia instancji mapy ogranicz do minimum pracę w wątku UI.
Debugowanie wycieków pamięci
Jeśli po wdrożeniu wszystkich odpowiednich sugestii opisanych wcześniej w tym dokumencie nadal występują wycieki pamięci, wykonaj te czynności, aby przeprowadzić debugowanie.
Zanim zaczniesz, zapoznaj się ze sposobem zarządzania pamięcią przez Androida. Więcej informacji znajdziesz w artykule Omówienie zarządzania pamięcią na Androidzie.
Aby debugować wycieki pamięci, wykonaj te czynności:
- Wykonaj czynności prowadzące do wystąpienia problemu. Ten krok jest niezbędny do debugowania.
- Sprawdź, czy wykorzystanie pamięci jest zgodne z oczekiwaniami. Sprawdź, czy zwiększone wykorzystanie, które wygląda na wyciek, nie jest w rzeczywistości pamięcią wymaganą do uruchomienia aplikacji.
- Debugowanie na wysokim poziomie. Do debugowania możesz użyć kilku narzędzi. Do debugowania problemów z pamięcią w Androidzie służą 3 różne standardowe zestawy narzędzi: Android Studio, Perfetto i narzędzia wiersza poleceń Android Debug Bridge (adb).
- Sprawdź wykorzystanie pamięci przez aplikację. pobrać zrzut stosu i śledzenie alokacji, a następnie je przeanalizować;
- Naprawianie wycieków pamięci
W kolejnych sekcjach szczegółowo opisujemy te kroki.
Krok 1. Wykonaj czynności prowadzące do wystąpienia problemu
Jeśli nie udało Ci się odtworzyć problemu, najpierw rozważ scenariusze, które mogły doprowadzić do wycieku pamięci. Jeśli wiesz, że problem został odtworzony, możesz od razu przejść do analizy zrzutu sterty. Jeśli jednak zrzut stosu zostanie wygenerowany podczas uruchamiania aplikacji lub w innym losowym momencie, warunki wycieku mogą nie zostać aktywowane. Podczas próby odtworzenia problemu rozważ różne scenariusze:
Jakie funkcje są aktywowane?
Jaka konkretna sekwencja działań użytkownika powoduje wyciek?
- Czy próbowałeś(-aś) aktywować tę sekwencję na różne sposoby?
Jakie stany cyklu życia przeszła aplikacja?
- Czy próbowano wielokrotnie przechodzić przez różne stany cyklu życia?
Upewnij się, że możesz odtworzyć problem w najnowszej wersji pakietów SDK. Problem z poprzedniej wersji mógł już zostać rozwiązany.
Krok 2. Sprawdź, czy wykorzystanie pamięci przez aplikację jest zgodne z oczekiwanym
Każda funkcja wymaga dodatkowej pamięci. Podczas debugowania różnych scenariuszy zastanów się, czy może to być oczekiwane użycie, czy też jest to wyciek pamięci. Na przykład w przypadku różnych funkcji lub zadań użytkownika rozważ te możliwości:
Prawdopodobny wyciek: aktywowanie scenariusza w ramach wielu iteracji powoduje wzrost wykorzystania pamięci w czasie.
Prawdopodobnie oczekiwane wykorzystanie pamięci: pamięć jest odzyskiwana po zatrzymaniu scenariusza.
Prawdopodobnie oczekiwane wykorzystanie pamięci: wykorzystanie pamięci wzrasta przez pewien czas, a następnie maleje. Może to być spowodowane ograniczoną pamięcią podręczną lub innym oczekiwanym wykorzystaniem pamięci.
Jeśli zachowanie aplikacji jest prawdopodobnie oczekiwanym wykorzystaniem pamięci, problem można rozwiązać, zarządzając pamięcią aplikacji. Więcej informacji znajdziesz w artykule Zarządzanie pamięcią aplikacji.
Krok 3. Debugowanie na wysokim poziomie
Podczas debugowania wycieku pamięci zacznij od ogólnego poziomu, a gdy zawęzisz możliwości, przejdź do szczegółów. Użyj jednego z tych ogólnych narzędzi do debugowania, aby najpierw przeanalizować, czy wyciek występuje w czasie:
Memory Profiler w Android Studio (zalecane)
Memory Profiler w Android Studio
To narzędzie wyświetla histogram wykorzystania pamięci. Zrzuty stosu i śledzenie alokacji można też uruchamiać z tego samego interfejsu. To narzędzie jest domyślną rekomendacją. Więcej informacji znajdziesz w artykule Memory Profiler w Android Studio.
Liczniki pamięci Perfetto
Perfetto zapewnia precyzyjną kontrolę nad śledzeniem kilku danych i prezentuje je wszystkie na jednym histogramie. Więcej informacji znajdziesz w artykule na temat liczników pamięci Perfetto.

Narzędzia wiersza poleceń Android Debug Bridge (adb)
Większość danych, które można śledzić za pomocą Perfetto, jest też dostępna jako narzędzie wiersza poleceń adb, które można wyszukiwać bezpośrednio. Oto kilka ważnych przykładów:
Meminfo pozwala wyświetlić szczegółowe informacje o pamięci w danym momencie.
Procstats udostępnia ważne zagregowane statystyki z czasem.
Kluczową statystyką, na którą należy zwrócić uwagę, jest maksymalne wykorzystanie pamięci fizycznej (maxRSS), którego aplikacja wymaga w czasie. MaxPSS może nie być tak dokładny. Aby zwiększyć dokładność, zapoznaj się z flagą adb shell dumpsys procstats --help –start-testing.
Śledzenie przydziału
Śledzenie alokacji identyfikuje zrzut stosu, w którym pamięć została przydzielona i nie została zwolniona. Ten krok jest szczególnie przydatny podczas śledzenia wycieków w kodzie natywnym. To narzędzie identyfikuje zrzut stosu, więc może być świetnym sposobem na szybkie debugowanie głównej przyczyny lub ustalenie, jak odtworzyć problem. Instrukcje korzystania ze śledzenia alokacji znajdziesz w artykule Debugowanie pamięci w kodzie natywnym za pomocą śledzenia alokacji.
Krok 4. Sprawdź wykorzystanie pamięci przez aplikację za pomocą zrzutu stosu
Jednym ze sposobów wykrywania wycieku pamięci jest uzyskanie zrzutu sterty aplikacji i sprawdzenie go pod kątem wycieków. Zrzut stosu to zrzut wszystkich obiektów w pamięci aplikacji. Może służyć do diagnozowania wycieków pamięci i innych problemów z pamięcią.
Android Studio może wykrywać wycieki pamięci, których nie można naprawić za pomocą GC. Gdy przechwycisz zrzut stosu, Android Studio sprawdza, czy istnieje aktywność lub fragment, które są nadal dostępne, ale zostały już zniszczone.
Szczegółowe informacje znajdziesz w sekcjach poniżej.
Przechwytywanie zrzutu stosu
Aby przechwycić zrzut sterty, możesz użyć Android Debug Bridge (adb) lub narzędzia Memory Profiler w Android Studio.
Przechwytywanie zrzutu stosu za pomocą adb
Aby przechwycić zrzut stosu za pomocą adb, wykonaj te czynności:
- Podłącz urządzenie z Androidem do komputera.
- Otwórz wiersz polecenia i przejdź do katalogu, w którym znajdują się narzędzia adb.
Aby przechwycić zrzut sterty, uruchom to polecenie :
adb shell am dumpheap my.app.name $PHONE_FILE_OUTAby pobrać zrzut sterty, uruchom to polecenie:
adb pull $PHONE_FILE_OUT $LOCAL_FILE.
Przechwytywanie zrzutu stosu za pomocą Android Studio
Aby przechwycić zrzut sterty za pomocą narzędzia Memory Profiler w Android Studio, wykonaj czynności opisane w sekcji Przechwytywanie zrzutu sterty na stronie poświęconej Androidowi.
Analizowanie zrzutu sterty w celu znalezienia wycieków pamięci
Po przechwyceniu zrzutu stosu możesz go przeanalizować za pomocą narzędzia Memory Profiler w Android Studio. W tym celu wykonaj następujące czynności:
Otwórz projekt aplikacji na Androida w Android Studio.
Kliknij Uruchom, a następnie wybierz konfigurację Debugowanie.
Otwórz kartę Android Profiler.
Wybierz Pamięć.
Kliknij Otwórz zrzut stosu i wybierz wygenerowany plik zrzutu stosu. Profiler pamięci wyświetla wykres wykorzystania pamięci przez aplikację.
Użyj wykresu, aby przeanalizować zrzut stosu:
Zidentyfikuj obiekty, które nie są już używane.
identyfikować obiekty, które zużywają dużo pamięci;
Sprawdź, ile pamięci używa każdy obiekt.
Użyj tych informacji, aby zawęzić zakres lub znaleźć źródło wycieku pamięci i go naprawić.
Krok 5. Napraw wycieki pamięci
Gdy zidentyfikujesz źródło wycieku pamięci, możesz je naprawić. Naprawianie wycieków pamięci w aplikacjach na Androida pomaga poprawić ich wydajność i stabilność. Szczegóły różnią się w zależności od scenariusza. Możesz jednak skorzystać z tych wskazówek:
Upewnij się, że aplikacja przydziela i zwalnia pamięć zgodnie z zaleceniami podanymi w artykule Zarządzanie pamięcią aplikacji na temat Androida.
Usuń z aplikacji nieużywany kod lub zasoby. Szczegółowe informacje o aplikacjach na Androida znajdziesz w artykule Sprawdzone metody dotyczące aplikacji na Androida.
Inne narzędzia do debugowania
Jeśli po wykonaniu tych czynności nadal nie udało Ci się znaleźć i naprawić wycieku pamięci, wypróbuj te narzędzia:
- Debugowanie pamięci w kodzie natywnym za pomocą śledzenia alokacji
- Wykrywanie wycieków za pomocą LeakCanary
Debugowanie pamięci w kodzie natywnym za pomocą śledzenia alokacji
Nawet jeśli nie używasz bezpośrednio kodu natywnego, robi to kilka popularnych bibliotek Androida, w tym pakiety SDK Google. Jeśli uważasz, że wyciek pamięci występuje w kodzie natywnym, możesz użyć kilku narzędzi do debugowania. Śledzenie alokacji za pomocą Android Studio lub heapprofd (zgodnego również z Perfetto) to świetny sposób na zidentyfikowanie potencjalnych przyczyn wycieku pamięci i często najszybsza metoda debugowania.
Śledzenie alokacji ma też tę zaletę, że pozwala udostępniać wyniki bez dołączania informacji poufnych, które można znaleźć na stosie.
Identyfikowanie wycieków za pomocą LeakCanary
LeakCanary to zaawansowane narzędzie do identyfikowania wycieków pamięci w aplikacjach na Androida. Więcej informacji o używaniu LeakCanary w aplikacji znajdziesz na stronie LeakCanary.
Jak zgłaszać problemy z pakietami SDK Google
Jeśli po wypróbowaniu metod opisanych w tym dokumencie podejrzewasz, że w naszych pakietach SDK występuje wyciek pamięci, skontaktuj się z obsługą klienta i podaj jak najwięcej z tych informacji:
Kroki umożliwiające odtworzenie wycieku pamięci. Jeśli kroki wymagają złożonego kodowania, możesz skopiować kod, który odtwarza problem, do naszej przykładowej aplikacji i podać dodatkowe kroki, które należy wykonać w interfejsie, aby wywołać wyciek.
Zrzuty stosu z aplikacji z odtworzonym problemem. Zrób zrzuty stosu w 2 różnych momentach, które pokazują, że wykorzystanie pamięci znacznie wzrosło.
Jeśli spodziewasz się wycieku pamięci natywnej, udostępnij dane wyjściowe śledzenia alokacji z heapprofd.
Raport o błędzie wygenerowany po odtworzeniu warunków wycieku.
Zrzuty stosu awarii związanych z pamięcią.
Ważna uwaga: zrzuty stosu zwykle nie wystarczają do debugowania problemu z pamięcią, dlatego pamiętaj, aby podać też inne informacje.