Auf dieser Seite wird erläutert, wie Sie die Speichernutzung in Ihrer App proaktiv reduzieren. Informationen dazu, wie das Android-Betriebssystem den Arbeitsspeicher verwaltet, finden Sie unter Überblick zur Speicherverwaltung.
Der Arbeitsspeicher (Random Access Memory, RAM) ist eine wertvolle Ressource für jede Softwareentwicklungsumgebung und noch wertvoller für ein mobiles Betriebssystem, bei dem der physische Arbeitsspeicher oft begrenzt ist.
Obwohl sowohl die Android Runtime (ART) als auch die Dalvik-virtuelle Maschine eine routinemäßige Garbage Collection ausführen, bedeutet das nicht, dass Sie ignorieren können, wann und wo Ihre App Speicher zuweist und freigibt.
Sie müssen weiterhin Speicherlecks vermeiden, die in der Regel durch das Festhalten von Objektreferenzen in statischen Mitgliedsvariablen verursacht werden, und alle Reference
-Objekte zum richtigen Zeitpunkt freigeben, wie von den Lebenszyklus-Callbacks definiert.
Verfügbare Arbeitsspeichernutzung und Arbeitsspeichernutzung überwachen
Sie müssen die Speichernutzungsprobleme Ihrer App ermitteln, bevor Sie sie beheben können. Die Memory Profiler in Android Studio hilft Ihnen, und können Speicherprobleme auf folgende Arten diagnostizieren:
- Sehen Sie sich an, wie Ihre App im Laufe der Zeit Speicher zuweist. Der Memory Profiler zeigt in einem Echtzeitdiagramm, viel Arbeitsspeicher, den Ihre App verwendet, die Anzahl der zugewiesenen Java-Objekte und den Zeitpunkt der automatischen Speicherbereinigung. erfolgt.
- Starten Sie Garbage-Collection-Ereignisse und erstellen Sie einen Snapshot des Java-Haufens, während Ihre App ausgeführt wird.
- Speicherbelegung der App aufzeichnen, alle zugewiesenen Objekte prüfen, Stacktrace ansehen und springen Sie im Android Studio-Editor zum entsprechenden Code.
Speicherplatz aufgrund von Ereignissen freigeben
Android kann bei Bedarf Speicherplatz von Ihrer App zurückfordern oder die App vollständig beenden, um Speicherplatz für kritische Aufgaben freizugeben, wie im Abschnitt Übersicht über die Speicherverwaltung erläutert. Weitere Hilfe
den Systemspeicher auszugleichen und zu vermeiden, dass das System den App-Prozess beenden muss, können Sie
die
ComponentCallbacks2
in Ihren Activity
-Klassen.
Das angegebene
onTrimMemory()
-Rückrufmethode informiert Ihre App über Lebenszyklus- oder speicherbezogene Ereignisse, die eine gute
die Möglichkeit, die Speichernutzung
für deine App freiwillig zu reduzieren. Durch das Freigeben von Arbeitsspeicher lässt sich die Wahrscheinlichkeit verringern, dass Ihre App vom Low-Memory-Killer beendet wird.
Sie können den onTrimMemory()
-Callback implementieren, um auf verschiedene speicherbezogene Ereignisse zu reagieren, wie im folgenden Beispiel gezeigt:
Kotlin
import android.content.ComponentCallbacks2 // Other import statements. class MainActivity : AppCompatActivity(), ComponentCallbacks2 { // Other activity code. /** * Release memory when the UI becomes hidden or when system resources become low. * @param level the memory-related event that is raised. */ override fun onTrimMemory(level: Int) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } }
Java
import android.content.ComponentCallbacks2; // Other import statements. public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 { // Other activity code. /** * Release memory when the UI becomes hidden or when system resources become low. * @param level the memory-related event that is raised. */ public void onTrimMemory(int level) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } }
Bestimmen, wie viel Arbeitsspeicher Sie benötigen
Um mehrere laufende Prozesse zuzulassen, legt Android für jeden Prozess ein festes Limit für die Heap-Größe fest
Das genaue Limit für die Heap-Größe hängt davon ab, wie viel RAM das Gerät hat
insgesamt. Wenn Ihre App die Heap-Kapazität erreicht und versucht, mehr Speicher zuzuweisen,
ein OutOfMemoryError
.
Damit der Arbeitsspeicher nicht vollläuft, können Sie das System abfragen, um festzustellen, wie viel Heap-Speicher auf dem aktuellen Gerät verfügbar ist. Sie können das System nach dieser Abbildung abfragen, indem Sie
getMemoryInfo()
Dadurch wird ein
ActivityManager.MemoryInfo
Objekt, das Informationen zum aktuellen Speicherstatus des Geräts bereitstellt, einschließlich verfügbarer
Arbeitsspeicher, Gesamtarbeitsspeicher und der Arbeitsspeicherschwellenwert, d. h. der Arbeitsspeicherlevel, bei dem das System
Prozesse zu stoppen. Das ActivityManager.MemoryInfo
-Objekt gibt auch lowMemory
zurück, eine einfache Boolesche Variable, die angibt, ob auf dem Gerät nur noch wenig Speicherplatz verfügbar ist.
Im folgenden Beispielcode-Snippet sehen Sie, wie die Methode getMemoryInfo()
in
für Ihre App.
Kotlin
fun doSomethingMemoryIntensive() { // Before doing something that requires a lot of memory, // check whether the device is in a low memory state. if (!getAvailableMemory().lowMemory) { // Do memory intensive work. } } // Get a MemoryInfo object for the device's current memory status. private fun getAvailableMemory(): ActivityManager.MemoryInfo { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager return ActivityManager.MemoryInfo().also { memoryInfo -> activityManager.getMemoryInfo(memoryInfo) } }
Java
public void doSomethingMemoryIntensive() { // Before doing something that requires a lot of memory, // check whether the device is in a low memory state. ActivityManager.MemoryInfo memoryInfo = getAvailableMemory(); if (!memoryInfo.lowMemory) { // Do memory intensive work. } } // Get a MemoryInfo object for the device's current memory status. private ActivityManager.MemoryInfo getAvailableMemory() { ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); activityManager.getMemoryInfo(memoryInfo); return memoryInfo; }
Speichereffizientere Codekonstrukte verwenden
Einige Android-Funktionen, Java-Klassen und Codekonstrukte verbrauchen mehr Arbeitsspeicher als andere. Sie können den Arbeitsspeicherverbrauch Ihrer App minimieren, indem Sie effizientere Alternativen in Ihrem Code verwenden.
Dienste sparsam nutzen
Wir empfehlen Ihnen dringend, Dienste nicht laufen zu lassen, wenn sie nicht benötigt werden. Unnötige Dienste laufen zu lassen, ist einer der schlimmsten Fehler bei der Arbeitsspeicherverwaltung, den eine Android-App machen kann. Wenn Ihre App einen Dienst benötigt, der im Hintergrund ausgeführt werden kann, ausgeführt werden, es sei denn, es muss einen Job ausführen. Beenden Sie den Dienst, wenn die Aufgabe abgeschlossen ist. Andernfalls kann es zu einem Speicherleck kommen.
Wenn Sie einen Dienst starten, behält das System den Prozess für diesen Dienst am Laufen. Dieses erschwert Dienstprozesse, weil der von einem Dienst verwendete RAM für andere Prozesse nicht verfügbar. Dadurch wird die Anzahl der im LRU-Cache gespeicherten Prozesse reduziert, was den App-Wechsel weniger effizient macht. Dies kann sogar zu einem System-Trashing führen, wenn der Arbeitsspeicher knapp ist und das System nicht genügend Prozesse aufrechterhalten kann, um alle derzeit ausgeführten Dienste zu hosten.
Vermeiden Sie im Allgemeinen die Verwendung persistenter Dienste aufgrund der anhaltenden Anforderungen an verfügbare Dienste
zu speichern. Wir empfehlen stattdessen eine alternative Implementierung, z. B.
WorkManager
Weitere Informationen zum Planen von Hintergrundprozessen mit WorkManager
finden Sie unter Dauerhafte Arbeit.
Optimierte Datencontainer verwenden
Einige der in der Programmiersprache verfügbaren Kurse sind nicht für Mobilgeräte optimiert
Geräte. Zum Beispiel könnte die allgemeine
HashMap
-Implementierung kann Arbeitsspeicher sein
ineffizient, da für jede Zuordnung ein separates Eintragsobjekt erforderlich ist.
Das Android-Framework umfasst mehrere optimierte Datencontainer, darunter:
SparseArray
,
SparseBooleanArray
,
und LongSparseArray
.
Die SparseArray
-Klassen sind beispielsweise effizienter, da das System den Schlüssel und manchmal auch den Wert nicht automatisch in ein Feld einfügen muss, wodurch ein oder zwei weitere Objekte pro Eintrag erstellt werden.
Bei Bedarf können Sie jederzeit zu Rohlisten für eine schlanke Datenstruktur wechseln.
Vorsicht bei Codeabstraktionen
Entwickelnde verwenden Abstraktionen oft als gute Programmierpraxis, da sie den Code verbessern können Flexibilität und Wartung. Abstraktionen sind jedoch wesentlich kostspieliger, benötigen in der Regel mehr Code, der ausgeführt werden muss, wodurch mehr Zeit und RAM zum in den Speicher ein. Wenn Ihre Abstraktionen keinen großen Nutzen haben, sollten Sie sie vermeiden.
Lite-Protobufs für serialisierte Daten verwenden
Protokoll Puffer (protobufs) sind ein sprach-, plattformneutraler, erweiterbarer Mechanismus, Google zum Serialisieren strukturierter Daten – ähnlich wie XML, aber kleiner, schneller und einfacher. Wenn Sie protobufs für Ihre Daten verwenden, verwenden Sie in Ihrem clientseitigen Code immer Lite-Protobufs. Regulär protobufs generieren extrem ausführlichen Code, was zu vielen Problemen in Ihrer App führen kann, erhöhte RAM-Nutzung, deutliche Erweiterung der APK-Größe und langsamere Ausführung.
Weitere Informationen finden Sie in der Protobuf lesen.
Arbeitsspeicherabwanderung vermeiden
Ereignisse der Garbage Collection wirken sich nicht auf die Leistung Ihrer App aus. Viele Garbage-Collection-Ereignisse, die innerhalb kurzer Zeit auftreten, können jedoch den Akku schnell entladen und die Zeit für die Einrichtung von Frames aufgrund notwendiger Interaktionen zwischen dem Garbage Collector und den App-Threads geringfügig verlängern. Je mehr Zeit das System mit der automatischen Speicherbereinigung verbringt, desto schneller entlädt sich.
Häufig kann eine Speicherabwanderung zu einer großen Anzahl von Ereignissen zur automatischen Speicherbereinigung führen. In der Praxis beschreibt die Arbeitsspeicherauslastung die Anzahl der zugewiesenen temporären Objekte, die in einem bestimmten Zeitraum auftreten.
Sie können beispielsweise mehrere temporäre Objekte innerhalb einer for
-Schleife zuweisen. Alternativ können Sie neue Paint
- oder Bitmap
-Objekte innerhalb der onDraw()
-Funktion einer Ansicht erstellen. In beiden Fällen erstellt die App schnell viele Objekte in großem Volumen. Diese
schnell den gesamten verfügbaren Speicher der jungen Generation verbrauchen, wodurch eine automatische Speicherbereinigung erzwungen wird.
Ereignis stattfinden soll.
Verwenden Sie den Memory Profiler, um die Orte in wenn die Speicherfluktuation hoch ist, bevor Sie sie beheben können.
Nachdem Sie die Problembereiche in Ihrem Code identifiziert haben, versuchen Sie, die Anzahl der Zuweisungen innerhalb leistungskritische Bereiche. Du könntest Dinge aus inneren Schlaufen herausholen oder sie in eine werksbasiert die Zuweisungsstruktur.
Sie können auch prüfen, ob Objektpools für den Anwendungsfall geeignet sind. Bei einem Objektpool legen Sie eine Objektinstanz nicht auf den Boden, sondern geben sie in einen Pool frei, wenn sie nicht mehr benötigt wird. Wenn das nächste Mal eine Objektinstanz dieses Typs benötigt wird, können Sie sie aus dem Pool abrufen, anstatt sie zuzuweisen.
Bewerten Sie die Leistung gründlich, um festzustellen, ob ein Objektpool in einer bestimmten Situation geeignet ist. Es gibt Fälle, in denen Objektpools die Leistung beeinträchtigen können. Obwohl Pools kollidieren, führen sie zu anderen Gemeinkosten. Zum Beispiel umfasst die Wartung des Pools mit einem nicht vernachlässigbaren Mehraufwand. Außerdem wird die Objektinstanz gelöscht, zur Vermeidung von Speicherlecks während der Veröffentlichung. Außerdem kann die Initialisierung bei der Akquisition einen Wert ungleich null haben. koordiniert.
Wenn mehr Objektinstanzen im Pool gehalten werden, als erforderlich sind, wird auch die Garbage Collection belastet. Objektpools reduzieren zwar die Anzahl der Aufrufe der Garbage Collection, erhöhen aber den Arbeitsaufwand für jeden Aufruf, da dieser proportional zur Anzahl der aktiven (erreichbaren) Bytes ist.
Speicherintensive Ressourcen und Bibliotheken entfernen
Einige Ressourcen und Bibliotheken in Ihrem Code können Arbeitsspeicher belegen, ohne dass Sie es merken. Die die Gesamtgröße deiner App, einschließlich Drittanbieterbibliotheken und eingebetteten Ressourcen, beeinflussen kann, wie viel Arbeitsspeicher die Anwendung verbraucht. Sie können den Speicherverbrauch Ihrer App verbessern, indem Sie redundante, unnötige oder aufgeblähte Komponenten oder Ressourcen und Bibliotheken aus Ihrem Code.
Gesamtgröße des APKs reduzieren
Sie können die Arbeitsspeichernutzung Ihrer App erheblich reduzieren, indem Sie die Gesamtgröße der App verringern. Bitmap-Größe, Ressourcen, Animationsframes und Bibliotheken von Drittanbietern können zur Größe beitragen. Ihrer App. Android Studio und das Android SDK bieten mehrere Tools, mit denen Sie die Größe von und externen Abhängigkeiten. Diese Tools unterstützen moderne Methoden zur Codeverkleinerung, z. B. R8-Kompilierung.
Weitere Informationen zum Reduzieren der App-Gesamtgröße finden Sie unter Reduziere die App-Größe.
Hilt oder Dagger 2 für die Abhängigkeitsinjektion verwenden
Abhängigkeitsinjektions-Frameworks können den von Ihnen geschriebenen Code vereinfachen und eine adaptive die für Tests und andere Konfigurationsänderungen nützlich ist.
Wenn Sie ein Abhängigkeitsinjektions-Framework in Ihrer App verwenden möchten, sollten Sie Hilt oder Dog. Hilt ist eine Dependency Injection-Bibliothek für Android, die auf Dagger basiert. Dagger verwendet keine Reflexion, um die Code. Sie können die statische Compilezeitimplementierung von Dagger in Android-Apps verwenden, ohne unnötige Laufzeitkosten oder Speichernutzung.
Andere Abhängigkeitsinjektions-Frameworks, die Reflexion zum Initialisieren von Prozessen verwenden, für Annotationen. Dieser Vorgang kann wesentlich mehr CPU-Zyklen und RAM erfordern und zu einer deutlichen Verzögerung beim Starten der App führen.
Vorsicht bei der Verwendung externer Bibliotheken
Externer Bibliothekscode wird häufig nicht für mobile Umgebungen geschrieben und kann für die an einem mobilen Client arbeiten. Wenn Sie eine externe Bibliothek verwenden, müssen Sie diese möglicherweise für Mobilgeräte. Planen Sie diese Arbeit im Voraus und analysieren Sie die Bibliothek im Hinblick auf die Codegröße und den RAM-Speicherplatz benötigt, bevor Sie ihn verwenden.
Selbst einige für Mobilgeräte optimierte Bibliotheken können aufgrund unterschiedlicher Implementierungen zu Problemen führen. So kann beispielsweise eine Bibliothek Lite-Protobufs verwenden, während eine andere Micro-Protobufs verwendet, was zu zwei verschiedenen Protobuf-Implementierungen in Ihrer App führt. Dies kann bei verschiedenen Implementierungen von Logging, Analysen, Bildlade-Frameworks, Caching und vielen anderen Dingen auftreten, die Sie nicht erwarten.
ProGuard kann dabei helfen, APIs und Ressourcen mit
die richtigen Flags gesetzt sind, können die
großen internen Abhängigkeiten einer Bibliothek nicht entfernt werden. Für die gewünschten Funktionen in diesen Bibliotheken sind möglicherweise Abhängigkeiten auf niedrigerer Ebene erforderlich. Dies wird besonders problematisch, wenn Sie eine Activity
-Unterklasse aus einer Bibliothek verwenden, die viele Abhängigkeiten haben kann, wenn Bibliotheken Reflection verwenden, was häufig vorkommt und eine manuelle Anpassung von ProGuard erfordert, damit es funktioniert.
Verwenden Sie eine gemeinsam genutzte Bibliothek nicht nur für eine oder zwei von Dutzenden Funktionen. Laden Sie nicht zu viel Code und Overhead ein, den Sie nicht verwenden. Wenn Sie überlegen, ob Sie eine Bibliothek verwenden möchten, suchen Sie nach einer Implementierung, die Ihren Anforderungen weitgehend entspricht. Andernfalls können Sie Ihre eigene Implementierung erstellen.