Der Memory Profiler ist eine Komponente im Android Profiler, die Ihnen hilft, Speicherlecks und Speicherschwankungen zu erkennen, die zu Stottern, Einfrieren und sogar zum Absturz der App führen können. Er zeigt ein Echtzeitdiagramm der Speichernutzung Ihrer App an und ermöglicht es Ihnen, einen Heap-Dump zu erfassen, Garbage Collections zu erzwingen und Speicherzuweisungen zu verfolgen.
Um den Memory Profiler zu öffnen, gehen Sie folgendermaßen vor:
- Klicken Sie auf Ansicht > Werkzeugfenster > Profiler (Sie können auch auf Profil in der Symbolleiste klicken).
- Wählen Sie das Gerät und den App-Prozess, den Sie profilieren möchten, in der AndroidProfiler-Symbolleiste aus. Wenn Sie ein Gerät über USB angeschlossen haben, es aber nicht in der Liste erscheint, stellen Sie sicher, dass Sie das USB-Debugging aktiviert haben.
- Klicken Sie auf eine beliebige Stelle in der MEMORY-Zeitleiste, um den Memory Profiler zu öffnen.
Alternativ können Sie den Speicher Ihrer App von der Kommandozeile aus mitdumpsys untersuchen und auch GC-Ereignisse in logcat anzeigen lassen.
Warum Sie den Speicher Ihrer App profilieren sollten
Android bietet eine verwaltete Speicherumgebung – wenn es feststellt, dass Ihre App einige Objekte nicht mehr verwendet, gibt der Garbage Collector den ungenutzten Speicher zurück in den Heap. Die Art und Weise, wie Android ungenutzten Speicher findet, wird ständig verbessert, aber irgendwann muss das System bei allen Android-Versionen Ihren Code kurz unterbrechen. Die meiste Zeit über sind diese Pausen nicht wahrnehmbar. Wenn Ihre Anwendung jedoch schneller Speicher zuweist, als das System ihn sammeln kann, kann es zu Verzögerungen kommen, während der Collector genügend Speicher freigibt, um Ihre Zuweisungen zu erfüllen. Die Verzögerung kann dazu führen, dass Ihre Anwendung Frames überspringt und eine sichtbare Verlangsamung verursacht.
Selbst wenn Ihre Anwendung keine Verlangsamung zeigt, kann sie, wenn sie Speicher leckt, diesen Speicher behalten, selbst wenn sie im Hintergrund läuft. Dieses Verhalten kann die restliche Speicherleistung des Systems verlangsamen, da unnötige Garbage Collection-Ereignisse erzwungen werden. Schließlich ist das System gezwungen, Ihren Anwendungsprozess zu beenden, um den Speicher zurückzugewinnen. Wenn der Benutzer dann zu Ihrer Anwendung zurückkehrt, muss diese komplett neu gestartet werden.
Um diese Probleme zu vermeiden, sollten Sie den Memory Profiler verwenden, um Folgendes zu tun:
- Suchen Sie nach unerwünschten Speicherzuweisungsmustern in der Zeitleiste, die zu Leistungsproblemen führen könnten.
- Zeigen Sie den Java-Heap an, um zu sehen, welche Objekte zu einem bestimmten Zeitpunkt Speicher verbrauchen. Mehrere Heap-Dumps über einen längeren Zeitraum können helfen, Speicherlecks zu identifizieren.
- Zeichnen Sie Speicherzuweisungen während normaler und extremer Benutzerinteraktion auf, um genau zu erkennen, wo Ihr Code entweder zu viele Objekte in kurzer Zeit zuweist oder Objekte zuweist, die undicht werden.
Informationen zu Programmierpraktiken, die den Speicherverbrauch Ihrer Anwendung reduzieren können, finden Sie unter Verwalten des Speichers Ihrer Anwendung.
Überblick über den Memory Profiler
Wenn Sie den Memory Profiler zum ersten Mal öffnen, sehen Sie eine detaillierte Zeitleiste der Speichernutzung Ihrer Anwendung und haben Zugriff auf Tools, mit denen Sie die Garbage Collection erzwingen, einen Heapdump erfassen und Speicherzuweisungen aufzeichnen können.
Abbildung 1. Der Memory Profiler
Wie in Abbildung 1 dargestellt, enthält die Standardansicht des Memory Profiler Folgendes:
- Eine Schaltfläche zum Erzwingen einer Garbage Collection.
-
Eine Schaltfläche zum Erfassen eines Heapdumps.
Hinweis: Eine Schaltfläche zum Aufzeichnen von Speicherzuweisungen wird nur dann rechts neben der Heap-Dump-Schaltfläche angezeigt, wenn sie mit einem Gerät verbunden ist, auf dem Android 7.1 (API-Level 25) oder niedriger läuft.
- Ein Dropdown-Menü, mit dem Sie angeben können, wie häufig der Profiler Speicherzuweisungen aufzeichnet. Die Auswahl der entsprechenden Option kann Ihnen helfen, die App-Leistung während der Profilerstellung zu verbessern.
- Schaltflächen zum Vergrößern/Verkleinern der Zeitleiste.
- Eine Schaltfläche zum Vorwärtsspringen zu den Live-Speicherdaten.
- Die Ereigniszeitleiste, die die Aktivitätszustände, Benutzereingabeereignisse und Bildschirmdrehungsereignisse anzeigt.
- Die Zeitleiste der Speichernutzung, die Folgendes enthält:
- Ein gestapeltes Diagramm, das anzeigt, wie viel Speicher von jeder Speicherkategorie verwendet wird, wie durch die y-Achse auf der linken Seite und den Farbschlüssel oben angezeigt.
- Eine gestrichelte Linie zeigt die Anzahl der zugewiesenen Objekte an, wie durch die y-Achse auf der rechten Seite angezeigt.
- Ein Symbol für jedes Garbage Collection-Ereignis.
Wenn Sie jedoch ein Gerät mit Android 7.1 oder niedriger verwenden, sind nicht alle Profilierungsdaten standardmäßig sichtbar. Wenn Sie die Meldung „Erweiterte Profilerstellung ist für den ausgewählten Prozess nicht verfügbar“ sehen, müssen Sie die erweiterte Profilerstellung aktivieren, um Folgendes zu sehen:
- Ereigniszeitleiste
- Anzahl der zugewiesenen Objekte
- Ereignisse der Müllsammlung
Auf Android 8.0 und höher ist die erweiterte Profilerstellung für debuggingfähige Apps immer aktiviert.
Wie der Speicher gezählt wird
Die Zahlen, die Sie oben im Memory Profiler sehen (Abbildung 2), basieren auf allen privaten Speicherseiten, die Ihre App gemäß dem Android-System belegt hat. Diese Zählung umfasst keine Seiten, die mit dem System oder anderen Anwendungen geteilt werden.
Abbildung 2. Die Legende zur Speicheranzahl oben im Memory Profiler
Die Kategorien in der Speicheranzahl sind wie folgt:
- Java: Speicher von Objekten, die von Java- oder Kotlin-Code zugewiesen wurden.
-
Native: Speicher von Objekten, die aus C- oder C++-Code zugewiesen wurden.
Auch wenn Sie in Ihrer App kein C++ verwenden, wird hier möglicherweise etwas nativer Speicher verwendet, da das Android-Framework nativen Speicher verwendet, um verschiedene Aufgaben für Sie zu erledigen, z. B. bei der Verarbeitung von Bildelementen und anderen Grafiken – auch wenn der von Ihnen geschriebene Code in Java oder Kotlin vorliegt.
-
Grafik: Speicher, der für Grafikpuffer-Warteschlangen verwendet wird, um Pixel auf dem Bildschirm darzustellen, einschließlich GL-Oberflächen, GL-Texturen und so weiter. (Beachten Sie, dass es sich hierbei um gemeinsam mit der CPU genutzten Speicher handelt, nicht um dedizierten GPU-Speicher.)
-
Stack: Speicher, der sowohl von nativen als auch von Java-Stapeln in Ihrer Anwendung verwendet wird. Dies bezieht sich in der Regel darauf, wie viele Threads Ihre Anwendung ausführt.
-
Code: Speicher, den Ihre Anwendung für Code und Ressourcen verwendet, wie z. B. Dex-Bytecode, optimierter oder kompilierter Dex-Code, .so-Bibliotheken und Schriftarten.
-
Sonstiges: Von Ihrer Anwendung genutzter Speicher, den das System nicht genau kategorisieren kann.
-
Allokiert: Die Anzahl der von Ihrer Anwendung zugewiesenen Java/Kotlin-Objekte. Objekte, die in C oder C++ zugewiesen wurden, werden nicht gezählt.
Wenn Sie mit einem Gerät verbunden sind, auf dem Android 7.1 oder niedriger läuft, beginnt die Zählung der Zuweisungen erst zu dem Zeitpunkt, an dem der Memory Profiler eine Verbindung zu Ihrer laufenden App herstellt. Daher werden alle Objekte, die vor Beginn der Profilerstellung zugewiesen wurden, nicht berücksichtigt. Android 8.0 und höher enthält jedoch ein geräteinternes Profiling-Tool, das alle Zuweisungen verfolgt, so dass diese Zahl immer die Gesamtzahl der Java-Objekte darstellt, die in Ihrer App unter Android 8.0 und höher ausstehen.
Im Vergleich zu den Speicherzählungen des vorherigen Android Monitor-Tools zeichnet der neue Memory Profiler Ihren Speicher anders auf, so dass es den Anschein haben könnte, dass Ihre Speichernutzung jetzt höher ist. Der Memory Profiler überwacht einige zusätzliche Kategorien, die die Gesamtzahl erhöhen, aber wenn Sie sich nur für den Java-Heap-Speicher interessieren, sollte die „Java“-Zahl ähnlich wie der Wert des vorherigen Tools sein.Obwohl die Java-Zahl wahrscheinlich nicht genau mit dem übereinstimmt, was Sie in AndroidMonitor gesehen haben, berücksichtigt die neue Zahl alle physischen Speicherseiten, die dem Java-Heap Ihrer Anwendung zugewiesen wurden, seit sie von Zygote geforkt wurde. Dies bietet also eine genaue Darstellung, wie viel physischen Speicher Ihre App tatsächlich verwendet.
Speicherzuweisungen anzeigen
Speicherzuweisungen zeigen Ihnen, wie jedes Java-Objekt und jeder JNI-Verweis in Ihrem Speicher zugewiesen wurde. Insbesondere kann der Memory Profiler Folgendes über Objektzuweisungen anzeigen:
- Welche Objekttypen zugewiesen wurden und wie viel Speicherplatz sie verbrauchen.
- Der Stack-Trace jeder Zuweisung, einschließlich des Threads.
- Wann die Objekte freigegeben wurden (nur bei Verwendung eines Geräts mit Android 8.0 oder höher).
Wenn Ihr Gerät mit Android 8.0 oder höher läuft, können Sie Ihre Objektzuweisungen jederzeit wie folgt anzeigen: Ziehen Sie in der Zeitleiste, um den Bereich auszuwählen, für den Sie die Zuweisungen sehen möchten (wie in Video 1 gezeigt). Es ist nicht nötig, eine Aufzeichnungssitzung zu starten, da Android 8.0 und höher ein geräteunabhängiges Profiling-Tool enthält, das die Zuweisungen Ihrer App ständig verfolgt.
Video 1. Wählen Sie unter Android 8.0 und höher einen vorhandenen Zeitleistenbereich aus, um Objektzuweisungen anzuzeigen
Wenn Ihr Gerät mit Android 7.1 oder niedriger läuft, klicken Sie in der Memory Profiler-Symbolleiste auf Speicherzuweisungen aufzeichnen . Während der Aufzeichnung verfolgt derMemory Profiler alle Zuweisungen, die in Ihrer Anwendung auftreten. Wenn Sie fertig sind, klicken Sie auf Aufzeichnung beenden (dieselbe Schaltfläche; siehe Video 2), um die Zuweisungen anzuzeigen.
Video 2. Mit Android 7.1 und niedriger müssen Sie Speicherzuweisungen explizit aufzeichnen
Nachdem Sie einen Bereich der Zeitleiste ausgewählt haben (oder wenn Sie eine Aufzeichnungssitzung mit einem Gerät mit Android 7.1 oder niedriger läuft), wird die Liste der zugewiesenen Objekte unterhalb der Zeitleiste angezeigt, gruppiert nach Klassennamen und sortiert nach ihrer Heap-Anzahl.
Um den Zuweisungsdatensatz zu prüfen, führen Sie die folgenden Schritte aus:
- Durchsuchen Sie die Liste, um Objekte zu finden, die ungewöhnlich große Heap-Anzahlen haben und die möglicherweise ausgelaufen sind. Um bekannte Klassen zu finden, klicken Sie auf die Spaltenüberschrift Class Namecolumn, um die Liste alphabetisch zu sortieren. Klicken Sie dann auf einen Klassennamen. Auf der rechten Seite erscheint der Bereich Instanzansicht, in dem jede Instanz dieser Klasse angezeigt wird (siehe Abbildung 3).
- Alternativ können Sie Objekte schnell finden, indem Sie auf Filter klicken oder Strg+F (Befehl+F auf dem Mac) drücken und einen Klassen- oder Packagenamen in das Suchfeld eingeben. Sie können auch nach Methodennamen suchen, wenn Sie im Dropdown-Menü die Option Nach Aufrufstapel suchen auswählen. Wenn Sie reguläre Ausdrücke verwenden möchten, aktivieren Sie das Kästchen neben Regex. Aktivieren Sie das Kontrollkästchen nebenGroß-/Kleinschreibung beachten, wenn Ihre Suchabfrage die Groß-/Kleinschreibung berücksichtigt.
- Klicken Sie im Bereich Instanzansicht auf eine Instanz. Die Registerkarte Call Stack erscheint darunter und zeigt an, wo diese Instanz zugewiesen wurde und in welchem Thread.
- Klicken Sie in der Registerkarte Call Stack mit der rechten Maustaste auf eine beliebige Zeile und wählen SieJump to Source, um diesen Code im Editor zu öffnen.
Abbildung 3. Details über jedes zugewiesene Objekt werden in der Instanzansicht auf der rechten Seite angezeigt.
Mit den beiden Menüs über der Liste der zugewiesenen Objekte können Sie auswählen, welcher Heap inspiziert werden soll und wie die Daten organisiert werden sollen.
Wählen Sie aus dem Menü auf der linken Seite, welcher Heap inspiziert werden soll:
- Standard-Heap: Wenn kein Heap vom System angegeben wird.
- Image-Heap: Das System-Boot-Image, das Klassen enthält, die während des Bootvorgangs vorgeladen werden. Allokationen hier werden garantiert niemals verschoben oder verschwinden.
- zygote heap: Der Copy-on-Write-Heap, von dem aus ein App-Prozess im Android-System geforkt wird.
- app heap: Der primäre Heap, in dem Ihre App Speicher zuweist.
- JNI-Heap: Der Heap, der anzeigt, wo Java Native Interface (JNI)-Referenzen zugewiesen und freigegeben werden.
Wählen Sie aus dem Menü auf der rechten Seite, wie Sie die Zuweisungen anordnen möchten:
- Nach Klasse anordnen: Gruppiert alle Zuweisungen nach dem Klassennamen. Dies ist die Standardeinstellung.
- Nach Paket anordnen: Gruppiert alle Zuweisungen auf der Grundlage des Paketnamens.
- Nach Aufrufstapel anordnen: Gruppiert alle Zuweisungen in den entsprechenden Aufrufstapel.
Verbessern der App-Leistung bei der Profilerstellung
Um die App-Leistung bei der Profilerstellung zu verbessern, führt der Speicher-Profiler standardmäßig regelmäßige Stichproben bei den Speicherzuweisungen durch. Wenn Sie auf Geräten mit API-Level 26 oder höher testen, können Sie dieses Verhalten ändern, indem Sie das Dropdown-Menü Zuweisungsnachverfolgung verwenden. Folgende Optionen sind verfügbar:
- Vollständig: Erfasst alle Objektzuweisungen im Speicher. Dies ist das Standardverhalten in Android Studio 3.2 und früher. Wenn Sie eine App haben, die viele Objekte zuweist, können Sie bei der Profilerstellung sichtbare Verlangsamungen Ihrer App beobachten.
- Stichprobenartig: Nimmt Objektzuweisungen im Speicher in regelmäßigen Abständen auf. Dies ist die Standardoption und hat weniger Auswirkungen auf die App-Leistung bei der Profilerstellung. Apps, die viele Objekte über einen kurzen Zeitraum zuweisen, können dennoch sichtbare Verlangsamungen aufweisen.
- Aus: Beendet die Verfolgung der Speicherzuweisung Ihrer App.
Globale JNI-Referenzen anzeigen
Java Native Interface (JNI) ist ein Framework, das es Java-Code und nativem Code ermöglicht, sich gegenseitig aufzurufen.
JNI-Referenzen werden manuell vom nativen Code verwaltet, so dass es möglich ist, dass vom nativen Code verwendete Java-Objekte zu lange am Leben gehalten werden. Einige Objekte auf dem Java-Heap können unerreichbar werden, wenn eine JNI-Referenz verworfen wird, ohne zuvor explizit gelöscht worden zu sein. Außerdem ist es möglich, das Limit für globale JNI-Referenzen auszuschöpfen.
Um solche Probleme zu beheben, verwenden Sie die JNI-Heap-Ansicht im Memory Profiler, um alle globalen JNI-Referenzen zu durchsuchen und sie nach Java-Typen und nativen Callstacks zu filtern. Mit diesen Informationen können Sie herausfinden, wann und wo globale JNI-Referenzen erstellt und gelöscht werden.
Während Ihre Anwendung läuft, wählen Sie einen Teil der Zeitleiste aus, den Sie untersuchen möchten, und wählen Sie JNI-Heap aus dem Dropdown-Menü oberhalb der Klassenliste.Sie können dann die Objekte im Heap wie gewohnt untersuchen und auf der Registerkarte Zuweisungsaufrufstapel auf Objekte doppelklicken, um zu sehen, wo die JNI-Referenzen in Ihrem Code zugewiesen und freigegeben werden, wie in Abbildung 4 dargestellt.
Abbildung 4. Anzeigen von globalen JNI-Referenzen
Um die Speicherzuweisungen für den JNI-Code Ihrer App zu überprüfen, müssen Sie Ihre App auf einem Gerät mit Android 8.0 oder höher bereitstellen.
Weitere Informationen zu JNI finden Sie unter JNI-Tipps.
Native Memory Profiler
Der Android Studio Memory Profiler enthält einen Native Memory Profiler für Apps, die auf physischen Geräten mit Android 10 bereitgestellt werden; Unterstützung für Android 11-Geräte ist derzeit in der Android Studio 4.2-Vorschauversion verfügbar.
Der Native Memory Profiler verfolgt Zuweisungen/Deallokationen von Objekten in nativem Code für einen bestimmten Zeitraum und liefert die folgenden Informationen:
- Zuweisungen: Eine Zählung der Objekte, die über
malloc()
oder dennew
-Operator während des ausgewählten Zeitraums zugewiesen wurden. - Deallocations: Eine Zählung der Objekte, die über
free()
oder den Operatordelete
während des ausgewählten Zeitraums freigegeben wurden. - Zuweisungen Größe: Die aggregierte Größe in Bytes aller Zuweisungen während des ausgewählten Zeitraums.
- Deallocations Size: Die aggregierte Größe in Bytes aller freigegebenen Speicherbereiche während des ausgewählten Zeitraums.
- Gesamtanzahl: Der Wert in der Spalte „Zuweisungen“ minus dem Wert in der Spalte „Freigaben“.
- Verbleibende Größe: Der Wert in der Spalte Größe der Zuweisungen abzüglich des Wertes in der Spalte Größe der Freigaben.
Um eine Aufzeichnung zu starten, klicken Sie oben im Fenster des Memory Profiler auf Native Zuweisungen aufzeichnen:
Wenn Sie die Aufzeichnung beenden möchten, klicken Sie auf Aufzeichnung stoppen.
Standardmäßig verwendet der Native Memory Profiler eine Stichprobengröße von 32 Byte: Jedes Mal, wenn 32 Byte Speicher zugewiesen werden, wird ein Schnappschuss des Speichers erstellt. Eine kleinere Stichprobengröße führt zu häufigeren Schnappschüssen und damit zu präziseren Daten über die Speichernutzung. Eine größere Stichprobengröße liefert weniger genaue Daten, verbraucht aber weniger Ressourcen auf Ihrem System und verbessert die Leistung während der Aufzeichnung.
So ändern Sie die Stichprobengröße des Native Memory Profiler:
- Wählen Sie Ausführen > Konfigurationen bearbeiten.
- Wählen Sie Ihr Anwendungsmodul im linken Bereich.
- Klicken Sie auf die Registerkarte „Profiling“ und geben Sie die Stichprobengröße in das Feld „Abtastintervall für den Nativspeicher (Bytes)“ ein.
- Erstellen Sie Ihre Anwendung und führen Sie sie erneut aus.
Erfassen Sie einen Heap-Dump
Ein Heap-Dump zeigt, welche Objekte in Ihrer Anwendung zum Zeitpunkt der Erfassung des Heap-Dumps Speicher verwenden. Vor allem nach einer längeren Benutzersitzung kann ein Heap-Dump helfen, Speicherlecks zu identifizieren, indem er Objekte anzeigt, die sich noch im Speicher befinden, von denen Sie glauben, dass sie dort nicht mehr sein sollten.
Nach dem Erfassen eines Heap-Dumps können Sie Folgendes anzeigen:
- Welche Arten von Objekten Ihre Anwendung zugewiesen hat und wie viele von jedem.
- Wie viel Speicher jedes Objekt verwendet.
- Wo Verweise auf jedes Objekt in Ihrem Code gehalten werden.
- Der Aufrufstapel für den Ort, an dem ein Objekt zugeordnet wurde. (Aufrufstapel sind derzeit nur unter Android 7.1 und niedriger mit einem Heap-Dump verfügbar, wenn Sie den Heap-Dump während der Aufzeichnung von Zuweisungen erfassen.)
Um einen Heap-Dump zu erfassen, klicken Sie in der Memory Profiler-Symbolleiste auf Java-Heap dumpen.Während des Dumpens des Heaps kann sich der Java-Speicher vorübergehend vergrößern.Dies ist normal, da der Heap-Dump im selben Prozess wie Ihre Anwendung stattfindet und etwas Speicher zum Sammeln der Daten benötigt.
Der Heap-Dump wird unterhalb der Speicherzeitleiste angezeigt und zeigt alle Klassentypen im Heap, wie in Abbildung 5 dargestellt.
Abbildung 5. Anzeigen des Heap-Dumps
Wenn Sie genauer wissen wollen, wann der Dump erstellt wird, können Sie einen Heap-Dump an einem kritischen Punkt in Ihrem Anwendungscode erstellen, indem SiedumpHprofData()
aufrufen.
In der Liste der Klassen können Sie die folgenden Informationen sehen:
- Zuweisungen: Anzahl der Zuweisungen im Heap.
-
Native Größe: Gesamtmenge des von diesem Objekttyp verwendeten nativen Speichers (in Bytes). Diese Spalte ist nur für Android 7.0 und höher sichtbar.
Sie werden hier Speicher für einige in Java zugewiesene Objekte sehen, da Android nativen Speicher für einige Framework-Klassen verwendet, wie z.B.
Bitmap
. -
Shallow Size: Gesamtmenge des von diesem Objekttyp verwendeten Java-Speichers (in Bytes).
-
Retained Size: Gesamtgröße des durch alle Instanzen dieser Klasse beibehaltenen Speichers (in Bytes).
Über die beiden Menüs oberhalb der Liste der zugewiesenen Objekte können Sie auswählen, welche Heap-Dumps untersucht werden sollen und wie die Daten organisiert werden sollen.
Wählen Sie aus dem Menü auf der linken Seite, welchen Heap Sie untersuchen möchten:
- Standard-Heap: Wenn kein Heap vom System angegeben wird.
- app heap: Der primäre Heap, auf dem Ihre Anwendung Speicher zuweist.
- Image-Heap: Das System-Boot-Image, das Klassen enthält, die während des Bootvorgangs vorgeladen werden. Allokationen hier werden garantiert niemals verschoben oder verschwinden.
- zygote heap: Der Copy-on-Write-Heap, von dem aus ein App-Prozess im Android-System geforkt wird.
Wählen Sie aus dem Menü auf der rechten Seite aus, wie Sie die Zuweisungen anordnen möchten:
- Nach Klassen anordnen: Gruppiert alle Zuweisungen nach dem Klassennamen. Dies ist die Standardeinstellung.
- Nach Paket anordnen: Gruppiert alle Zuweisungen auf der Grundlage des Paketnamens.
- Nach Aufrufstapel anordnen: Gruppiert alle Zuweisungen in ihren entsprechenden Aufrufstapel. Diese Option funktioniert nur, wenn Sie den Heap-Dump während der Aufzeichnung von Zuweisungen erfassen. Auch in diesem Fall ist es wahrscheinlich, dass sich Objekte im Heap befinden, die vor dem Beginn der Aufzeichnung zugewiesen wurden. Diese Zuweisungen werden daher zuerst angezeigt und einfach nach Klassennamen aufgelistet.
Die Liste wird standardmäßig nach der Spalte Retained Size sortiert. Um nach den Werten in einer anderen Spalte zu sortieren, klicken Sie auf die Spaltenüberschrift.
Klicken Sie auf einen Klassennamen, um das Fenster Instanzansicht auf der rechten Seite zu öffnen (siehe Abbildung 6). Jede aufgelistete Instanz enthält die folgenden Angaben:
- Depth: Die kürzeste Anzahl von Sprüngen von einer GC-Root zur ausgewählten Instanz.
- Native Größe: Diese Spalte ist nur für Android 7.0 und höher sichtbar.
- Shallow Size: Größe dieser Instanz im Java-Speicher.
- Retained Size: Größe des Speichers, den diese Instanz dominiert (gemäß dem Dominatorbaum).
Abbildung 6. Die Dauer, die für die Erfassung eines Heapdumps benötigt wird, ist in der Zeitleiste angegeben.
Um Ihren Heap zu untersuchen, gehen Sie wie folgt vor:
- Durchsuchen Sie die Liste, um Objekte zu finden, die eine ungewöhnlich große Heap-Anzahl haben und die möglicherweise ausgelaufen sind. Um bekannte Klassen zu finden, klicken Sie auf die Spaltenüberschrift „Klassenname“, um die Liste alphabetisch zu sortieren. Klicken Sie dann auf einen Klassennamen. Der Bereich Instanzansicht wird auf der rechten Seite angezeigt und zeigt jede Instanz dieser Klasse an, wie in Abbildung 6 dargestellt.
- Alternativ können Sie Objekte schnell finden, indem Sie auf Filter klicken oder Strg+F (Befehl+F auf Mac) drücken und einen Klassen- oder Paketnamen in das Suchfeld eingeben. Sie können auch nach Methodennamen suchen, wenn Sie im Dropdown-Menü die Option Nach Aufrufstapel sortieren auswählen. Wenn Sie reguläre Ausdrücke verwenden möchten, aktivieren Sie das Kästchen neben Regex. Aktivieren Sie das Kontrollkästchen nebenGroß-/Kleinschreibung beachten, wenn Ihre Suchabfrage die Groß-/Kleinschreibung berücksichtigt.
- Klicken Sie im Bereich Instanzansicht auf eine Instanz. Die Referenztabelle wird unten angezeigt und zeigt alle Verweise auf dieses Objekt an.
Oder klicken Sie auf den Pfeil neben dem Instanznamen, um alle seine Felder anzuzeigen, und klicken Sie dann auf einen Feldnamen, um alle Verweise anzuzeigen. Wenn Sie die Instanzdetails für ein Feld anzeigen möchten, klicken Sie mit der rechten Maustaste auf das Feld und wählen Sie Gehe zu Instanz.
- Wenn Sie auf der Registerkarte Verweise einen Verweis identifizieren, der möglicherweise Speicherplatz belegt, klicken Sie mit der rechten Maustaste darauf und wählen Sie Gehe zu Instanz. Dadurch wird die entsprechende Instanz aus dem Heap-Dump ausgewählt und ihre eigenen Instanzdaten werden angezeigt.
Suchen Sie in Ihrem Heap-Dump nach Speicherlecks, die durch einen der folgenden Punkte verursacht werden:
- Langlebige Referenzen auf
Activity
,Context
,View
,Drawable
und andere Objekte, die eine Referenz auf denActivity
– oderContext
-Container enthalten könnten. - Nicht statische innere Klassen, wie z. B.
Runnable
, die eineActivity
-Instanz enthalten können. - Caches, die Objekte länger als nötig halten.
Speichern Sie einen Heap-Dump als HPROF-Datei
Nach der Erfassung eines Heap-Dumps sind die Daten im Memory Profiler nur sichtbar, während der Profiler ausgeführt wird. Wenn Sie die Profiler-Sitzung beenden, verlieren Sie den Heap-Dump. Wenn Sie ihn also zur späteren Überprüfung speichern möchten, exportieren Sie den Heap-Dump in eine HPROF-Datei. In Android Studio 3.1 und niedriger befindet sich die Schaltfläche Exportiere Capture in Datei auf der linken Seite der Symbolleiste unterhalb der Zeitleiste; in Android Studio 3.2 und höher befindet sich die Schaltfläche Exportiere Heap-Dump rechts neben jedem Heap-Dump-Eintrag im Sitzungsfenster. Speichern Sie die Datei im Dialog Exportieren als mit der Dateinamenserweiterung .hprof
.
Um einen anderen HPROF-Analyzer wie jhat zu verwenden, müssen Sie die HPROF-Datei vom Android-Format in das Java SE HPROF-Format konvertieren, was Sie mit dem Tool hprof-conv
im Verzeichnis android_sdk/platform-tools/
tun können. Führen Sie den Befehl hprof-conv
mit zwei Argumenten aus: die ursprüngliche HPROF-Datei und den Speicherort für die konvertierte HPROF-Datei. Beispiel:
hprof-conv heap-original.hprof heap-converted.hprof
Importieren einer Heap-Dump-Datei
Um eine HPROF-Datei (.hprof
) zu importieren, klicken Sie im BereichSitzungen auf Neue Profiling-Sitzung starten, wählen Sie Aus Datei laden und wählen Sie die Datei im Dateibrowser aus.
Sie können eine HPROF-Datei auch importieren, indem Sie sie aus dem Dateibrowser in ein Editorfenster ziehen.
Erkennung von Lecks im Memory Profiler
Bei der Analyse eines Heap-Dumps im Memory Profiler können Sie Profildaten filtern, von denen Android Studio glaubt, dass sie auf Speicherlecks für Activity
undFragment
Instanzen in Ihrer App hinweisen könnten.
Zu den Datentypen, die der Filter anzeigt, gehören die folgenden:
-
Activity
Instanzen, die zerstört wurden, aber noch referenziert werden. -
Fragment
Instanzen, die keine gültigeFragmentManager
haben, aber noch referenziert werden.
In bestimmten Situationen, wie z. B. den folgenden, kann der Filter falsch-positive Ergebnisse liefern:
- Ein
Fragment
wurde erstellt, aber noch nicht verwendet. - Ein
Fragment
wird zwischengespeichert, aber nicht als Teil einesFragmentTransaction
.
Um diese Funktion zu verwenden, erfassen Sie zunächst einen Heap-Dump oder importieren Sie eine Heap-Dump-Datei in Android Studio. Um die Fragmente und Aktivitäten anzuzeigen, die möglicherweise Speicherlecks aufweisen, aktivieren Sie das Kontrollkästchen Activity/Fragment Leaks im Heapdump-Fenster des Memory Profiler, wie in Abbildung 7 dargestellt.
Abbildung 7. Filtern eines Heapdumps nach Speicherlecks.
Techniken für die Profilerstellung Ihres Speichers
Während Sie den Memory Profiler verwenden, sollten Sie Ihren Anwendungscode stressen und versuchen, Speicherlecks zu finden. Eine Möglichkeit, Speicherlecks in Ihrer Anwendung zu provozieren, besteht darin, sie eine Zeit lang laufen zu lassen, bevor Sie den Heap inspizieren. Lecks können bis an die Spitze der Allokationen im Heap sickern. Je kleiner das Leck jedoch ist, desto länger müssen Sie die Anwendung laufen lassen, um es zu sehen.
Sie können ein Speicherleck auch auf eine der folgenden Arten auslösen:
- Drehen Sie das Gerät in verschiedenen Aktivitätszuständen mehrmals vom Hoch- ins Querformat und wieder zurück. Das Drehen des Geräts kann oft dazu führen, dass eine App ein
Activity
,Context
oderView
Objekt verliert, weil das System dasActivity
neu erstellt und wenn Ihre App irgendwo anders einen Verweis auf eines dieser Objekte hält, kann das System es nicht abholen. - Wechseln Sie zwischen Ihrer App und einer anderen App, während Sie sich in verschiedenen Aktivitätszuständen befinden (navigieren Sie zumHome-Bildschirm und kehren Sie dann zu Ihrer App zurück).
Tipp: Sie können die oben genannten Schritte auch mit demonkeyrunner testframework durchführen.