Jedes Mal, wenn Ihre Anwendung einen neuen Thread erstellt, muss das Betriebssystem Arbeitsspeicher dafür zuweisen. Threads gelten zwar im Vergleich zu vollständigen Prozessen als „leichtgewichtig“, aber ihre Speicherkosten sind nicht gleich null. Threads, die nicht ordnungsgemäß beendet werden, sind eine sehr häufige Ursache für Speicherlecks auf Android-Geräten.
Kosten eines Threads
Wenn ein Thread auf Android-Geräten erstellt wird, verbrauchen zwei Hauptkomponenten Arbeitsspeicher:
- Der native Stack: Die
pthreadBibliothek weist einen Stack für den Thread zu um lokale Variablen und Aufruf-Frames zu speichern. Auf Android-Geräten ist das standardmäßig in der Regel 1 MB. - Interne JVM-/Kernel-Strukturen: ART und der Linux-Kernel
müssen Datenstrukturen wie
pthread_internal_tundjava.lang.Thread-Objekte zuweisen, um den Thread zu verfolgen und zu verwalten.
Stack-Arbeitsspeicher und mmap
Android weist den 1 MB großen nativen Stack mit mmap und den Flags MAP_ANONYMOUS | MAP_NORESERVE zu.
Das bedeutet, dass das Betriebssystem 1 MB virtuellen Arbeitsspeicheradressraum reserviert, aber nicht sofort 1 MB physischen RAM zuweist. Physischer Arbeitsspeicher wird nur zugewiesen (ausgelagert), wenn der Thread tatsächlich Code ausführt und Daten auf den Stack verschiebt. Ein Thread, der nichts tut, verbraucht nur sehr wenig physischen RAM für seinen Stack.
Praktische Übung: Threads, die nicht ordnungsgemäß beendet werden
Wir verwenden die Beispielanwendung MemoryLab , um zu beobachten, was passiert, wenn eine Anwendung Hunderte von inaktiven Threads erstellt.
1. Starten und Baseline
Starten Sie die App und lesen Sie die Baseline-Speicherauslastung aus:
adb shell am start -W -n com.android.memorylab/.MainActivity
adb shell dumpsys meminfo -s com.android.memorylab
Sehen Sie sich in der Ausgabe die Zeilen Stack und Native Heap an. Sie sind wahrscheinlich klein.
2. 100 Threads erstellen
Tippen Sie auf die Schaltfläche Create 100 Threads. Die Anwendung erstellt 100 neue Threads. Damit der Stack-Arbeitsspeicher tatsächlich in den physischen RAM ausgelagert wird (damit wir ihn in unseren Tools sehen können), weist jeder Thread ein kleines 10 KB großes Array auf seinem Stack zu und schreibt Daten hinein, bevor er mit dem Warten beginnt.
Führen Sie den Befehl „meminfo“ noch einmal aus, während die Threads aktiv sind:
adb shell dumpsys meminfo -s com.android.memorylab
Ergebnisse :
Vergleichen Sie die neue Ausgabe mit Ihrer Baseline. Sie werden feststellen, dass die Werte deutlich gestiegen sind, solange die Threads aktiv sind:
Stack: Um ein Megabyte oder mehr gestiegen. Dies entspricht den physischen Seiten, die für die 100 Thread-Stacks zugewiesen wurden (jeder mit mindestens 10 KB).Native Heap/Private Other: Aufgrund der internenpthread-Strukturen gestiegen, die vom System für jeden Thread zugewiesen wurden.
Wenn Sie die Threads beenden möchten, tippen Sie auf die Schaltfläche Destroy All Threads.
3. Threads in Perfetto ansehen
Perfetto eignet sich hervorragend, um den Lebenszyklus und die Anzahl von Threads im Zeitverlauf zu verfolgen.

PerfettoSQL für die Thread-Analyse
Sie können die Trace-Daten abfragen, um genau zu ermitteln, wie viele Threads von der Anwendung erstellt wurden:
SELECT p.name AS process_name, COUNT(t.id) as thread_count
FROM thread t JOIN process p USING (upid)
WHERE p.name = 'com.android.memorylab' AND t.name LIKE 'LeakedThread-%';
Mit einer solchen Abfrage lässt sich ein Debug-Track mit einem Zähler erstellen, wie im Screenshot gezeigt.
← App-Code | ↑ Nach oben | Lokalität →