Sprawdzone metody zarządzania pamięcią

W tym dokumencie zakładamy, że zastosowano sprawdzone metody dotyczące zarządzania pamięcią w aplikacjach na Androida, np. Zarządzanie pamięcią aplikacji.

Wprowadzenie

Wyciek pamięci to rodzaj wycieku zasobów, który występuje, gdy program komputerowy nie zwalnia już niepotrzebnej pamięci. Wyciek pamięci może spowodować, że aplikacja żąda od systemu więcej pamięci, niż ma dostępnej, i w konsekwencji się zawiesza. Szereg nieodpowiednich praktyk mogą powodować wycieki pamięci w aplikacjach na Androida, np. nieprawidłową utylizację zasobów lub nie wyrejestrowywać detektorów, gdy nie są już potrzebne.

W tym dokumencie znajdziesz sprawdzone metody zapobiegania, wykrywania i rozwiązywania przecieków pamięci w kodzie. Jeśli wypróbowałeś te metody i podejrzewać wyciek pamięci w naszych pakietach SDK, zobacz Jak zgłaszać problemy z pakietami SDK Google

Zanim skontaktujesz się z zespołem pomocy

Zanim zgłosisz wyciek pamięci do zespołu pomocy Google, postępuj zgodnie ze wskazówkami i instrukcjami debugowania podanymi w tym dokumencie, aby upewnić się, że błąd nie występuje w Twoim kodzie. Te czynności mogą rozwiązać problem. Jeśli nie, wygenerują informacje, których potrzebuje zespół pomocy Google, aby Ci pomóc.

Zapobieganie wyciekom pamięci

Postępuj zgodnie z tymi sprawdzonymi metodami, aby uniknąć niektórych 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 wykonałeś/wykonałaś wszystkie te czynności:

  1. Zwolnij nieużywane zasoby.
  2. Wyrejestruj słuchaczy, gdy nie są już potrzebni.
  3. Anulowanie niepotrzebnych zadań
  4. Przekieruj metody cyklu życia, aby zwolnić zasoby.
  5. Używaj najnowszych wersji pakietów SDK

Szczegółowe informacje o każdej z tych metod znajdziesz w następnych sekcjach.

Zwolnij nieużywane zasoby

Gdy aplikacja na Androida używa zasobu, pamiętaj, aby go zwolnić, gdy nie jest już potrzebny. Jeśli tego nie zrobisz, zasób nadal będzie zajmować pamięć nawet po ich zakończeniu. Więcej informacji znajdziesz w dokumentacji Androida na temat cyklu życia aktywności.

Uwolnienie nieaktualnych odwołań do GoogleMap w GeoSDK

Typowym błędem jest to, że GoogleMap może spowodować wyciek pamięci, jeśli jest przechowywany w pamięci podręcznej za pomocą NavigationView lub MapView. Mapa Google jest w relacji 1:1 z NavigationView lub MapView, z którego jest pobierany. Ty musi zapewnić, że mapa Google nie jest przechowywana w pamięci podręcznej, lub że odwołanie jest uruchamiany po wywołaniu NavigationView#onDestroy lub MapView#onDestroy. Jeśli za pomocą fragmentów NavigationSupportFragment, MapSupportFragment lub własnego fragmentu. ujęć te widoki, plik referencyjny musi zostać udostępniony 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
  }
}

rejestrowanie i odrejestrowanie słuchaczy, gdy nie są już potrzebne;

Gdy aplikacja na Androida zarejestruje słuchacza zdarzenia, np. kliknięcie przycisku lub zmianę stanu widoku, pamiętaj, aby zarejestrować słuchacza, gdy aplikacja nie będzie już musiała monitorować zdarzenia. Jeśli tego nie zrobisz, odbiorcy będą nadal zajmować pamięć nawet po zakończeniu działania aplikacji.

Załóżmy na przykład, że aplikacja używa pakietu Navigation SDK i wywołuje następujący detektor zdarzeń przyjazdu: addArrivalListener nasłuchuje zdarzeń przyjazdu, powinien również wywołać removeArrivalListener , gdy nie musi już monitorować zdarzeń przyjazdu.

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()
}

Anuluj niepotrzebne zadania

Gdy aplikacja na Androida rozpocznie zadanie asynchroniczne, takie jak pobieranie lub żądanie sieciowe, pamiętaj, aby je anulować po jego zakończeniu. Jeśli zadanie nie zostanie anulowane, będzie nadal działać w tle, nawet gdy aplikacja zakończyła już z nim pracę.

Więcej informacji o sprawdzonych metodach znajdziesz tutaj: Zarządzanie pamięcią aplikacji w dokumentacji Androida.

Przekierowuj metody cyklu życia, aby zwolnić zasoby

Jeśli Twoja aplikacja korzysta z pakietu SDK Nawigacji lub Map, pamiętaj, aby zwolnić zasoby, przekazując metody cyklu życia (wyróżnione pogrubioną czcionką) do navView. Dostępne opcje zrób to za pomocą NavigationView w pakiecie Navigation SDK, MapView w Mapach lub Pakiet SDK do nawigacji. Możesz też użyć SupportNavigationFragment lub SupportMapFragment zamiast bezpośrednio korzystać z NavigationView i MapView, . Fragmenty obsługujące obsługują przekazywanie 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ą nieustannie uzupełniane o nowe funkcje, poprawki błędów poprawy wydajności. Aktualizuj pakiety SDK w aplikacji, aby otrzymywać te dane naprawiania błędów.

Debugowanie wycieków pamięci

Jeśli po zastosowaniu wszystkich sugestii z początku tego dokumentu nadal występuje wyciek pamięci, wykonaj te czynności, aby przeprowadzić debugowanie.

Zanim zaczniesz, dowiedz się, jak Android zarządza pamięcią. Więcej informacji znajdziesz w sekcji dotyczącej Androida Omówienie zarządzania pamięcią

Aby debugować wycieki pamięci:

  1. Odtwórz problem. Ten krok jest niezbędny do debugowania.
  2. Sprawdź, czy wykorzystanie pamięci jest oczekiwane Sprawdź, czy zwiększone wykorzystanie, które wydaje się być wyciekiem, nie jest w istocie pamięcią wymaganą do działania aplikacji.
  3. Debuguj na poziomie ogólnym. Do debugowania można użyć kilku narzędzi. Do debugowania problemów z pamięcią w Androidzie służą 3 standardowe zestawy narzędzi: Android Studio, Perfeto i narzędzia wiersza poleceń Android Debug Bridge (adb).
  4. Sprawdź wykorzystanie pamięci przez aplikację. Pobierz zrzut stosu i śledzenie alokacji, a następnie przeanalizuj te dane.
  5. Naprawienie wycieków pamięci.

W następnych sekcjach znajdziesz szczegółowe instrukcje.

Krok 1. Odtwórz problem

Jeśli nie udało Ci się odtworzyć problemu, najpierw zastanów się, jakie scenariusze mogą prowadzić do wycieku pamięci. Jeśli wiesz, że problem został odtworzony, możesz przejść od razu do analizy zrzutu pamięci. Jeśli jednak zrzut stosu zostanie utworzony tylko przy uruchamianiu aplikacji lub w innym losowym momencie, może to oznaczać, że nie zostały spełnione warunki powodujące wyciek pamięci. Podczas próby odtworzenia problemu rozważ przetestowanie różnych scenariuszy:

  • Jaki zestaw funkcji jest aktywowany?

  • Jaka konkretna sekwencja działań użytkownika powoduje wyciek?

    • Czy próbowałeś/próbowałaś aktywować tę sekwencję kilka razy?
  • Przez które stany cyklu życia została przeprowadzona aplikacja?

    • Czy przetestowałeś/przetestowałaś już różne stany cyklu życia?

Sprawdź, czy możesz odtworzyć problem w najnowszej wersji pakietów SDK. być może został już rozwiązany.

Krok 2. Sprawdź, czy aplikacja wykorzystuje oczekiwane wykorzystanie pamięci

Każda funkcja wymaga dodatkowej pamięci. Podczas debugowania różnych scenariuszy zastanów się, czy jest to oczekiwane użycie, czy też wyciek pamięci. Na przykład w przypadku różnych funkcji lub zadań użytkownika weź pod uwagę następujące możliwości:

  • Prawdopodobna nieszczelność: aktywowanie scenariusza przez wiele iteracji powoduje z czasem wzrost wykorzystania pamięci.

  • Prawdopodobny oczekiwany poziom wykorzystania pamięci: pamięć jest odzyskiwana po zatrzymaniu scenariusza.

  • Możliwe oczekiwane wykorzystanie pamięci: wykorzystanie pamięci zwiększa się w okresie: a potem traci ważność. Może to być spowodowane ograniczoną pamięcią podręczną lub innymi oczekiwanymi wykorzystanie pamięci.

Jeśli działanie aplikacji jest prawdopodobnie oczekiwane wykorzystanie pamięci, problem może być taki: przez zarządzanie pamięcią aplikacji. Aby uzyskać pomoc, zapoznaj się z artykułem Zarządzanie pamięcią aplikacji

Krok 3. Debugowanie na wysokim poziomie

Debugowanie wycieku pamięci zacznij od najwyższego poziomu, a następnie przejdź do bardziej szczegółowego widoku po zawężeniu zakresu możliwości. Aby sprawdzić, czy występuje wyciek danych, najpierw użyj jednego z tych ogólnych narzędzi debugowania:

Narzędzie Memory Profiler w Android Studio

Jest to graficzny histogram wykorzystywanej pamięci. Zrzuty stosu i śledzenie alokacji można też aktywować w tym samym interfejsie. Ten to domyślne narzędzie. Więcej informacji znajdziesz w profilu pamięci w Android Studio.

Liczniki pamięci w Perfetto

Perfetto zapewnia dokładną kontrolę nad śledzeniem wielu rodzajów danych i prezentuje je w jednym histogramie. Więcej informacji znajdziesz w artykule liczniki pamięci Perfeto.

Interfejs Perfetto

Narzędzia wiersza poleceń Android Debug Bridge (adb)

Wiele elementów, które można śledzić za pomocą Perfetto, jest również dostępnych jako adb narzędzia wiersza poleceń, do którego można wysyłać zapytania. Oto kilka ważnych przykładów:

  • Meminfo: wyświetlać szczegółowe informacje o pamięci w określonym momencie.

  • Procstats zapewnia ważne statystyki zbiorcze na przestrzeni czasu.

Kluczową statystyką, na którą należy tu zwrócić uwagę, jest maksymalny rozmiar pamięci fizycznej (maxRSS), którego aplikacja wymaga z upływem czasu. Wartość MaxPSS może nie być tak dokładna. Dla: sposób na zwiększenie dokładności, flaga adb shell dumpsys procstats --help –start-testing.

Śledzenie alokacji

Śledzenie alokacji identyfikuje zrzut stosu, gdzie przydzielono pamięć i jeśli nie został uwolniony. Ten krok jest szczególnie przydatny przy śledzeniu wycieków kodu natywnego. Ponieważ to narzędzie identyfikuje ślad stosu, może być ono przydatne do szybkiego debugowania głównej przyczyny problemu lub dowiedzieć się, jak go odtworzyć. Instrukcje dotyczące korzystania z ś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 wykrycia wycieku pamięci jest pobranie zrzutu stosu aplikacji i sprawdzenie, czy nie ma w niej wycieku pamięci. Zrzut stosu to zrzut wszystkich obiektów w pamięci aplikacji. Może służyć do diagnozowania wycieków pamięci i innych związane z pamięcią.

Android Studio może wykrywać wycieki pamięci, których nie można naprawić przez GC. Podczas nagrywania zrzutu stosu, Android Studio sprawdza, czy występuje aktywność lub fragment który jest nadal osiągalny, ale został już zniszczony.

  1. Zarejestruj zrzut stosu.
  2. Przeanalizuj zrzut stosu, aby znaleźć wycieki pamięci.
  3. Naprawianie wycieków pamięci

Szczegółowe informacje znajdziesz w kolejnych sekcjach.

Przechwyć zrzut stosu

Aby wykonać zrzut stosu, możesz użyć narzędzia Android Debug Bridge (adb) lub narzędzia do profilowania pamięci w Android Studio.

Używanie narzędzia adb do przechwytywania zrzutu stosu

Aby wykonać zrzut stosu za pomocą adb, wykonaj te czynności:

  1. Podłącz urządzenie z Androidem do komputera.
  2. Otwórz wiersz polecenia i przejdź do katalogu, w którym znajdują się narzędzia adb.
  3. Aby uzyskać zrzut stosu, uruchom to polecenie:

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

  4. Aby pobrać zrzut stosu, uruchom to polecenie:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

Zrzut stosu za pomocą Android Studio

Aby wykonać zrzut stosu za pomocą narzędzia Android Studio Memory Profiler, wykonaj te czynności opisane w sekcji Zapisywanie zrzutu stosu.

Analizowanie zrzutu stosu w celu wykrywania wycieków pamięci

Po zrobieniu zrzutu stosu możesz użyć pamięci Android Studio program profilujący do przeanalizowania. W tym celu wykonaj następujące czynności:

  1. Otwórz projekt na Androida w Android Studio.

  2. Kliknij kolejno Uruchom i Debugowanie.

  3. Otwórz kartę Android Profiler.

  4. Wybierz Pamięć.

  5. Kliknij Otwórz zrzut stosu i wybierz wygenerowany plik zrzutu stosu. Profilator pamięci wyświetla wykres wykorzystania pamięci przez aplikację.

  6. Aby przeanalizować zrzut stosu, użyj wykresu:

    • identyfikować obiekty, których już nie używasz;

    • Identyfikowanie obiektów, które wykorzystują dużo pamięci.

    • Zobacz, ile pamięci używa każdy obiekt.

  7. Użyj tych informacji, aby zawęzić lub znaleźć źródło wycieku pamięci i je naprawić.

.

Krok 5. Napraw wycieki pamięci

Gdy poznasz źródło wycieku pamięci, możesz rozwiązać ten problem. Rozwiązanie problemu wycieków pamięci w aplikacjach na Androida pomaga zwiększyć wydajność i stabilność aplikacji. Szczegóły różnią się w zależności od sytuacji. Pomocne mogą być jednak te sugestie:

Inne narzędzia do debugowania

Jeśli po wykonaniu tych czynności nadal nie udało się znaleźć i rozwiązać problemu wyciek pamięci, wypróbuj te narzędzia:

Debuguj pamięć w kodzie natywnym ze śledzeniem alokacji

Nawet jeśli nie używasz bezpośrednio kodu natywnego, kilka popularnych bibliotek Androida łącznie z pakietami 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 (również zgodnego z Perfetto) to świetny sposób na zidentyfikowanie potencjalnych przyczyn wycieku pamięci. Jest to też często najszybsza metoda debugowania.

Śledzenie alokacji ma też tę zaletę, że umożliwia udostępnianie wyników bez uwzględniania informacji poufnych, które można znaleźć w stosie.

Identyfikuj wycieki za pomocą narzędzia LeakCanary

LeakCanary to zaawansowane narzędzie do identyfikowania wycieków pamięci w aplikacjach na Androida. Aby dowiedzieć się więcej o korzystaniu z LeakCanary w aplikacji, odwiedź stronę LeakCanary.

Jak zgłaszać problemy z pakietami SDK Google

Jeśli wypróbowano metody opisane w tym dokumencie i podejrzewasz wyciek pamięci skontaktuj się z działem obsługi klienta, podając jak najwięcej z poniższych informacji: jak to tylko możliwe:

  • Etapy odtwarzania wycieku pamięci. Jeśli czynności wymagają skomplikowanego kodowania, skopiuj kod, który odtwarza problem w naszej przykładowej aplikacji, i podaj dodatkowe czynności, które należy wykonać w interfejsie, aby wywołać wyciek.

  • zrzuty stosu z aplikacji z odtworzonym problemem. Utwórz zrzuty stosu w 2 różnych momentach, które pokazują, że wykorzystanie pamięci znacznie wzrosło.

  • Jeśli spodziewasz się wycieku pamięci natywnych funkcji, udostępnij dane wyjściowe śledzenia alokacji z heapprofd.

  • Raport o błędzie utworzony po odtworzeniu stanu wycieku.

  • Ślady stosu wszelkich awarii związanych z pamięcią.

    Ważna uwaga: zrzuty stosu zwykle nie wystarczają same w sobie debuguj problem z pamięcią, więc pamiętaj, by podać też jeden z pozostałych formularzy informacji.