Best Practices für die Speicherverwaltung

In diesem Dokument wird davon ausgegangen, dass Sie sich an Best Practices zur Speicherverwaltung für Android-Apps gehalten haben, die Sie z. B. auf dieser Seite finden.

Einleitung

Ein Speicherleck ist ein Ressourcenleck, das auftritt, wenn ein Computerprogramm den zugewiesenen Arbeitsspeicher, der nicht mehr benötigt wird, nicht freigibt. Ein Leck kann dazu führen, dass die App vom Betriebssystem mehr Arbeitsspeicher anfordert, als verfügbar ist – was wiederum den Absturz der App zur Folge hat. Verschiedene unsachgemäße Praktiken können bei Android-Apps Speicherlecks verursachen. Hierzu gehört z. B. die Nichtfreigabe von Ressourcen, die nicht mehr benötigt werden, oder auch die Aufrechterhaltung der Registrierung von nicht mehr benötigten Listenern.

Dieses Dokument enthält einige Best Practices, mit denen Sie Speicherlecks in Ihrem Code vermeiden, erkennen und beseitigen können. Wenn Sie die in diesem Dokument beschriebenen Methoden ausprobiert haben und ein Speicherleck in unseren SDKs vermuten, lesen Sie bitte den Abschnitt Probleme mit Google SDKs melden.

Bevor Sie sich an den Support wenden

Bevor Sie ein Speicherleck dem Google-Supportteam melden, folgen Sie bitte den Best Practices und den Schritten zur Fehlerbehebung in diesem Dokument, um sicherzugehen, dass der Fehler nicht in Ihrem Code enthalten ist. Mit diesen Schritten lässt sich das Problem eventuell beheben. Und falls nicht, werden die Informationen generiert, die das Google-Supportteam benötigt, um Ihnen weiterzuhelfen.

Speicherlecks vermeiden

Halten Sie sich an diese Best Practices, um einige der häufigsten Ursachen von Speicherlecks in Code zu vermeiden, der Google SDKs verwendet.

Best Practices für Android-Apps

Prüfen Sie, ob Sie in Ihrer Android-App jeden der folgenden Schritte ausgeführt haben:

  1. Nicht verwendete Ressourcen freigeben
  2. Registrierung von nicht mehr benötigten Listenern aufheben
  3. Nicht benötigte Tasks abbrechen
  4. Lebenszyklusmethoden weiterleiten, um Ressourcen freizugeben
  5. Neueste Versionen der SDKs verwenden

Genaue Details zu jedem dieser Schritte finden Sie in den folgenden Abschnitten.

Nicht verwendete Ressourcen freigeben

Wenn Ihre Android-App eine Ressource verwendet, geben Sie diese frei, sobald sie nicht mehr benötigt wird. Andernfalls belegt die Ressource weiterhin Arbeitsspeicher, auch nachdem sie von der App nicht mehr verwendet wird. Weitere Informationen finden Sie in diesem Artikel über den Aktivitätslebenszyklus in der Android Studio-Dokumentation.

Veraltete GoogleMap-Referenzen in GeoSDKs freigeben

Ein häufiger Fehler besteht darin, dass ein GoogleMap-Objekt ein Speicherleck verursachen kann, wenn es beim Verwenden der NavigationView oder MapView im Cache gespeichert wird. Ein GoogleMap-Objekt steht in einem 1:1-Verhältnis zur NavigationView oder MapView, aus der es abgerufen wird. Sie müssen dafür sorgen, dass ein GoogleMap-Objekt entweder nicht im Cache gespeichert wird oder der Verweis darauf freigegeben wird, wenn NavigationView#onDestroy oder MapView#onDestroy aufgerufen wird. Wenn Sie das NavigationSupportFragment, das MapSupportFragment oder Ihr eigenes Fragment verwenden, um diese Ansichten zusammenzufassen, muss der Verweis in Fragment#onDestroyView freigegeben werden.

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

Registrierung von nicht mehr benötigten Listenern aufheben

Wenn Ihre Android-App einen Listener für ein Ereignis registriert (z. B. das Klicken auf eine Schaltfläche oder eine Änderung des Ansichtsstatus), muss die Registrierung des Listeners aufgehoben werden, sobald die App das Ereignis nicht mehr überwachen muss. Andernfalls verbrauchen die Listener weiterhin Arbeitsspeicher, auch nachdem sie von der App nicht mehr verwendet werden.

Nehmen wir beispielsweise an, Ihre App verwendet das Navigation SDK und ruft den addArrivalListener auf, um Ankunftsereignisse zu erfassen. In diesem Fall sollte sie removeArrivalListener aufrufen, wenn die Ankunftsereignisse nicht mehr überwacht werden müssen.

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

Nicht benötigte Tasks abbrechen

Wenn eine Android-App eine asynchrone Task startet, z. B. einen Download oder eine Netzwerkanfrage, müssen Sie die Task nach der Erledigung abbrechen. Andernfalls wird sie, auch nachdem die App sie nicht mehr verwendet, weiterhin im Hintergrund ausgeführt.

Weitere Informationen zu den Best Practices finden Sie unter Verwaltung Ihres App-Speichers in der Android Studio-Dokumentation.

Lebenszyklusmethoden weiterleiten, um Ressourcen freizugeben

Wenn Ihre App das Navigation SDK oder das Maps SDK verwendet, geben Sie die Ressourcen frei, indem Sie Lebenszyklusmethoden (fett dargestellt) an navView weiterleiten. Hierzu können Sie NavigationView im Navigation SDK bzw. MapView im Maps SDK oder im Navigation SDK verwenden. Sie haben auch die Möglichkeit, SupportNavigationFragment oder SupportMapFragment statt direkt NavigationView bzw. MapView zu verwenden. Die unterstützenden Fragmente übernehmen die Weiterleitung der Lebenszyklusmethoden.

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

Neueste Versionen der SDKs verwenden

Google SDKs werden fortlaufend mit neuen Funktionen, Fehlerkorrekturen oder Leistungsverbesserungen aktualisiert. Halten Sie die SDKs in Ihrer App auf dem neuesten Stand, um alle Aktualisierungen zu erhalten.

Speicherlecks beheben

Wenn Sie alle weiter oben in diesem Dokument gemachten Vorschläge angewendet haben und trotzdem weiterhin Speicherlecks auftreten, gehen Sie wie nachfolgend beschrieben vor, um den Fehler zu beheben.

Bevor Sie beginnen, sollten Sie wissen, wie der Arbeitsspeicher von Android verwaltet wird. Lesen Sie dazu diesen Überblick zur Speicherverwaltung in Android.

So beseitigen Sie Speicherlecks:

  1. Reproduzieren Sie das Problem. Dieser Schritt ist für die Fehlerbehebung wichtig.
  2. Prüfen Sie, ob die Arbeitsspeichernutzung der Erwartung entspricht. Kontrollieren Sie, ob die vermeintlich auf ein Leck zurückzuführende erhöhte Speicherauslastung doch dem Arbeitsspeichervolumen entspricht, das zum Ausführen der App erforderlich ist.
  3. Beginnen Sie mit der Fehlersuche auf allgemeiner Ebene. Hierfür stehen Ihnen mehrere Dienstprogramme zur Verfügung. Es gibt drei Standardtools, mit denen sich Arbeitsspeicherprobleme in Android beheben lassen: Android Studio, Perfetto und die ADB-Befehlszeilendienstprogramme (Android Debug Bridge).
  4. Prüfen Sie die Speichernutzung Ihrer App. Rufen Sie einen Heap-Dump und die Arbeitsspeicherzuweisung ab und analysieren Sie dann das Ergebnis.
  5. Beheben Sie Speicherlecks.

In den folgenden Abschnitten werden diese Schritte ausführlich beschrieben.

Schritt 1: Problem reproduzieren

Wenn sich das Problem nicht reproduzieren ließ, überlegen Sie erst einmal, welche Szenarien zum Speicherleck führen könnten. Bei erfolgreicher Reproduktion des Problems ist es möglich, anhand eines Heap-Dumps die Lösung zu finden. Aber wenn Sie einfach beim Start der App oder zu einem anderen zufälligen Zeitpunkt einen Heap-Dump abrufen, sind nicht unbedingt die Bedingungen gegeben, die ein Speicherleck auslösen. Gehen Sie bei dem Versuch, das Problem zu reproduzieren, eventuell verschiedene Szenarien durch:

  • Welche Funktionen sind aktiviert?

  • Durch welche Abfolge von Nutzeraktionen wird das Speicherleck ausgelöst?

    • Haben Sie in mehreren Iterationen versucht, diese Abfolge zu aktivieren?
  • Welche Lebenszyklusstatus hat die App durchlaufen?

    • Haben Sie mehrere Iterationen in verschiedenen Lebenszyklusstatus ausprobiert?

Prüfen Sie, ob Sie das Problem mit der neuesten Version der SDKs reproduzieren können. Probleme aus einer früheren Version wurden möglicherweise bereits behoben.

Schritt 2: Prüfen, ob die Arbeitsspeichernutzung der App der Erwartung entspricht

Für jede Funktion wird zusätzlicher Arbeitsspeicher benötigt. Überlegen Sie bei der Fehlerbehebung in unterschiedlichen Szenarien, ob die erwartete Speicherauslastung oder tatsächlich ein Speicherleck vorliegt. Prüfen Sie beispielsweise bei verschiedenen Funktionen oder Nutzertasks die folgenden Möglichkeiten:

  • Wahrscheinlich ein Speicherleck: Beim Aktivieren des Szenarios über mehrere Iterationen hinweg erhöht sich mit der Zeit die Arbeitsspeichernutzung.

  • Wahrscheinlich die erwartete Arbeitsspeichernutzung: Nachdem das Szenario beendet wurde, wird Arbeitsspeicher freigegeben.

  • Möglicherweise die erwartete Arbeitsspeichernutzung: Die Arbeitsspeichernutzung nimmt eine Zeit lang zu und dann ab. Das könnte auf einen begrenzten Cache oder eine andere erwartete Arbeitsspeichernutzung zurückzuführen sein.

Wenn das Verhalten der App wahrscheinlich einer erwarteten Arbeitsspeichernutzung entspricht, lässt sich das Problem durch geeignetes Verwalten des Arbeitsspeichers der App beheben. Weitere Informationen dazu finden Sie unter diesem Link.

Schritt 3: Auf allgemeiner Ebene mit der Fehlersuche beginnen

Beginnen Sie bei der Suche nach einem Speicherleck auf allgemeiner Ebene und gehen Sie ins Detail, wenn Sie die Möglichkeiten eingegrenzt haben. Analysieren Sie mit einem der folgenden Debugging-Tools zuerst, ob nach einiger Zeit ein Speicherleck auftritt:

Memory Profiler von Android Studio

Mit diesem Tool erhalten Sie ein visuelles Histogramm des verbrauchten Arbeitsspeichers. Über die Benutzeroberfläche des Memory Profilers können Sie auch Heap-Dumps erfassen und die Arbeitsspeicherzuweisung abfragen. Dieses Tool ist die Standardempfehlung. Weitere Informationen finden Sie unter diesem Link.

Perfetto-Arbeitsspeicherzähler

Mit Perfetto können Sie mehrere Messwerte präzise im Zeitablauf erfassen und dies in einem einzigen Histogramm darstellen. Weitere Informationen finden Sie unter diesem Link.

Perfetto-Benutzeroberfläche

ADB-Befehlszeilendienstprogramme (Android Debug Bridge)

Vieles von dem, was sich mit Perfetto beobachten lässt, können Sie auch mit einem adb-Befehlszeilendienstprogramm direkt abfragen. Hier einige wichtige Beispiele:

  • Mit Meminfo können Sie detaillierte Informationen zum Arbeitsspeicher für einen bestimmten Zeitpunkt aufrufen.

  • Procstats bietet einige wichtige zusammengefasste Statistikparameter im Zeitverlauf.

Ein wichtiger statistischer Parameter, den Sie sich hier ansehen sollten, ist der maximale physische Speicherbedarf (maxRSS) der App im Zeitablauf. MaxPSS ist hier möglicherweise nicht so aussagekräftig. Informationen dazu, wie Sie aussagekräftigere Daten erhalten, finden Sie unter dem Flag adb shell dumpsys procstats --help –start-testing.

Nachverfolgung der Arbeitsspeicherzuweisung

Damit lässt sich der Stacktrace identifizieren, in dem Arbeitsspeicher zugewiesen wurde, und feststellen, ob der Speicher freigegeben wurde. Dieser Schritt ist besonders nützlich, wenn Sie Speicherlecks im nativen Code aufspüren möchten. Da mit diesem Tool der Stacktrace identifiziert wird, lässt sich damit unter Umständen die Ursache des Problems schnell beheben oder herausfinden, wie sich der Fehler reproduzieren lässt. Weitere Informationen zur Nachverfolgung der Arbeitsspeicherzuweisung finden Sie in diesem Abschnitt.

Schritt 4: Mit einem Heap-Dump die Arbeitsspeichernutzung der App prüfen

Eine Möglichkeit, ein Speicherleck zu finden, besteht darin, einen Heap-Dump Ihrer App abzurufen und ihn auf Lecks zu prüfen. Ein Heap-Dump ist eine Momentaufnahme von allen Objekten im Arbeitsspeicher der App. Damit lassen sich Speicherlecks und andere speicherbezogene Probleme diagnostizieren.

Android Studio ist in der Lage, Speicherlecks zu erkennen, die durch die automatische Speicherbereinigung nicht behoben werden können. Wenn Sie einen Heap-Dump erfassen, wird von Android Studio geprüft, ob es Aktivitäten oder Fragmente gibt, die noch erreichbar, aber bereits gelöscht sind.

  1. Heap-Dump erfassen
  2. Heap-Dump analysieren, um Speicherlecks zu finden
  3. Speicherlecks beheben

Details zu diesen Schritten finden Sie in den folgenden Abschnitten.

Heap-Dump erfassen

Einen Heap-Dump können Sie mit Android Debug Bridge (ADB) oder dem Memory Profiler von Android Studio erfassen.

Mit ADB einen Heap-Dump erfassen

So erfassen Sie mit ADB einen Heap-Dump:

  1. Verbinden Sie Ihr Android-Gerät mit dem Computer.
  2. Öffnen Sie die Eingabeaufforderung und rufen Sie das Verzeichnis auf, in dem sich die ADB-Tools befinden.
  3. Führen Sie den folgenden Befehl aus, um einen Heap-Dump zu erfassen:

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

  4. Zum Abrufen des Heap-Dumps führen Sie diesen Befehl aus:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

Mit Android Studio einen Heap-Dump erfassen

Führen Sie die unter diesem Link genannten Schritte aus, um mit dem Memory Profiler von Android Studio einen Heap-Dump zu erfassen:

Den Heap-Dump analysieren, um Speicherlecks zu finden

Nachdem Sie einen Heap-Dump erfasst haben, können Sie ihn mit dem Memory Profiler von Android-Studio analysieren. Gehen Sie dazu folgendermaßen vor:

  1. Öffnen Sie Ihr Android-Projekt in Android Studio.

  2. Wählen Sie Run (Ausführen) und dann die Debug-Konfiguration aus.

  3. Öffnen Sie den Tab Android Profiler.

  4. Wählen Sie Memory (Arbeitsspeicher) aus.

  5. Wählen Sie Open heap dump (Heap-Dump öffnen) und dann die von Ihnen erstellte Heap-Dump-Datei aus. Der Memory Profiler zeigt ein Diagramm der Arbeitsspeichernutzung Ihrer App.

  6. Analysieren Sie anhand des Diagramms den Heap-Dump:

    • Ermitteln Sie Objekte, die nicht mehr verwendet werden.

    • Ermitteln Sie Objekte, die viel Arbeitsspeicher verbrauchen.

    • Sehen Sie sich an, wie viel Arbeitsspeicher die einzelnen Objekte verbrauchen.

  7. Verwenden Sie diese Informationen, um die Ursache des Speicherlecks einzugrenzen oder zu identifizieren und sie zu beseitigen.

Schritt 5: Speicherlecks beheben

Sobald Sie die Ursache des Speicherlecks gefunden haben, können Sie sie beheben. Die Beseitigung von Speicherlecks in Android-Apps trägt zu besserer Leistung und größerer Stabilität der Apps bei. Die Details variieren zwar von Fall zu Fall, aber die folgenden Empfehlungen können helfen:

Sonstige Tools für die Fehlerbehebung

Wenn Sie die genannten Schritte durchgeführt haben und das Speicherleck trotzdem nicht finden und beseitigen konnten, probieren Sie es damit:

Arbeitsspeicherfehler in nativem Code durch Nachverfolgung der Speicherzuweisung beheben

Auch wenn Sie nicht direkt nativen Code verwenden – einige gängige Android-Bibliotheken und auch Google SDKs tun es. Wenn Sie das Speicherleck im nativen Code vermuten, gibt es mehrere Tools, die Sie zur Fehlerbehebung verwenden können. Die Nachverfolgung der Arbeitsspeicherzuweisung mit Android Studio oder heapprofd (kompatibel mit Perfetto) ist eine gute Möglichkeit, potenzielle Ursachen eines Speicherlecks zu erkennen, und oft der schnellste Weg zur Fehlerbehebung.

Diese Methode hat auch den wesentlichen Vorteil, dass Sie die Ergebnisse ohne in Heaps enthaltene vertrauliche Informationen weitergeben können.

Speicherlecks mit LeakCanary erkennen

LeakCanary ist ein leistungsstarkes Tool zur Erkennung von Speicherlecks in Android-Apps. Weitere Informationen zur Verwendung von LeakCanary in Apps finden Sie unter diesem Link.

Probleme mit Google SDKs melden

Wenn Sie die in diesem Dokument beschriebenen Methoden ausprobiert haben und ein Speicherleck in unseren SDKs vermuten, wenden Sie sich unter Angabe möglichst vieler der folgenden Informationen an den Kundensupport:

  • Schritte, um das Speicherleck zu reproduzieren: Wenn die Schritte eine komplexe Codierung erfordern, kann es hilfreich sein, den Code, mit dem das Problem reproduziert wird, in unsere Beispiel-App zu kopieren. Außerdem sollten Sie zusätzliche Schritte angeben, die in der Benutzeroberfläche ausgeführt werden müssen, um das Speicherleck auszulösen.

  • Heap-Dumps von Ihrer App nach der Reproduktion des Problems: Erfassen Sie Heap-Dumps zu zwei unterschiedlichen Zeitpunkten, zu denen die Arbeitsspeichernutzung erheblich zugenommen hat.

  • Wenn Sie ein Speicherleck im nativen Code vermuten, fügen Sie die heapprofd-Ergebnisse von der Nachverfolgung der Arbeitsspeicherzuweisung bei.

  • Fügen Sie einen Fehlerbericht bei, der erstellt wurde, nachdem Sie die Situation reproduziert haben, die zum Speicherleck geführt hat.

  • Stacktraces von Abstürzen, die mit dem Arbeitsspeicher zusammenhängen.

    Wichtiger Hinweis: Stacktraces allein genügen normalerweise nicht, um ein Problem mit dem Arbeitsspeicher zu beheben. Geben Sie deshalb mindestens noch eine der anderen Arten von Informationen an.