Arbeitsspeicher der App verwalten

Auf dieser Seite wird erläutert, wie Sie die Arbeitsspeichernutzung in Ihrer App proaktiv reduzieren können. Informationen zu wie das Android-Betriebssystem den Arbeitsspeicher verwaltet, siehe Arbeitsspeicherverwaltung – Übersicht

Random-Access Memory (RAM) ist eine wertvolle Ressource für jede Softwareentwicklungsumgebung. Für ein mobiles Betriebssystem, bei dem der physische Speicher häufig begrenzt ist, ist dies sogar noch wertvoller. Obwohl sowohl die Android Runtime (ART) als auch die virtuelle Maschine von Dalvik routinemäßigen Müll erheben können, bedeutet dies nicht, dass Sie ignorieren können, wann und wo Ihre App Arbeitsspeicher zuweist und freigibt. Sie müssen dennoch vermeiden, dass Speicherlecks auftreten. Diese werden normalerweise dadurch verursacht, dass Sie ein Objekt festhalten. Referenzen in statischen Member-Variablen – und geben Sie alle Reference Objekte bei Lebenszyklus-Callbacks zu einem bestimmten Zeitpunkt.

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 Arbeitsspeicher im Zeitverlauf 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.
  • Ereignisse für die automatische Speicherbereinigung initiieren und einen Snapshot des Java-Heaps erstellen, während Ihre App läuft ausgeführt wird.
  • Speicherbelegung der App aufzeichnen, alle zugewiesenen Objekte prüfen, Stacktrace ansehen und springen Sie im Android Studio-Editor zum entsprechenden Code.

Arbeitsspeicher als Reaktion auf Ereignisse freigeben

Android kann Arbeitsspeicher deiner App freigeben oder die App bei Bedarf vollständig beenden, um Arbeitsspeicher freizugeben für kritische Aufgaben, wie im Arbeitsspeicherverwaltung – Übersicht 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() Callback-Methode ermöglicht es Ihrer App, auf speicherbezogene Ereignisse zu warten, wenn sich Ihre App entweder im im Vordergrund oder im Hintergrund. Ihre App kann dann Objekte als Reaktion auf den App-Lebenszyklus Systemereignisse, die darauf hinweisen, dass das System Arbeitsspeicher freigeben muss.

Sie können den onTrimMemory()-Callback implementieren, um auf verschiedene speicherbezogene 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) {

        // Determine which lifecycle or system event is raised.
        when (level) {

            ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
                /*
                   Release any UI objects that currently hold memory.

                   The user interface moves to the background.
                */
            }

            ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE,
            ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
            ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> {
                /*
                   Release any memory your app doesn't need to run.

                   The device is running low on memory while the app is running.
                   The event raised indicates the severity of the memory-related event.
                   If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system
                   begins stopping background processes.
                */
            }

            ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
            ComponentCallbacks2.TRIM_MEMORY_MODERATE,
            ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> {
                /*
                   Release as much memory as the process can.

                   The app is on the LRU list and the system is running low on memory.
                   The event raised indicates where the app sits within the LRU list.
                   If the event is TRIM_MEMORY_COMPLETE, the process is one of the
                   first to be terminated.
                */
            }

            else -> {
                /*
                  Release any non-critical data structures.

                  The app receives an unrecognized memory level value
                  from the system. Treat this as a generic low-memory message.
                */
            }
        }
    }
}

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) {

        // Determine which lifecycle or system event is raised.
        switch (level) {

            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:

                /*
                   Release any UI objects that currently hold memory.

                   The user interface moves to the background.
                */

                break;

            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:

                /*
                   Release any memory your app doesn't need to run.

                   The device is running low on memory while the app is running.
                   The event raised indicates the severity of the memory-related event.
                   If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system
                   begins stopping background processes.
                */

                break;

            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:

                /*
                   Release as much memory as the process can.

                   The app is on the LRU list and the system is running low on memory.
                   The event raised indicates where the app sits within the LRU list.
                   If the event is TRIM_MEMORY_COMPLETE, the process is one of the
                   first to be terminated.
                */

                break;

            default:
                /*
                  Release any non-critical data structures.

                  The app receives an unrecognized memory level value
                  from the system. Treat this as a generic low-memory message.
                */
                break;
        }
    }
}

Überprüfen, wie viel Speicher 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.

Um zu verhindern, dass der Arbeitsspeicher ausgeht, können Sie das System abfragen, um zu ermitteln, wie viel Heap-Speicherplatz vorhanden ist. auf dem aktuellen Gerät verfügbar sind. Sie können das System nach dieser Zahl 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 Objekt ActivityManager.MemoryInfo macht außerdem lowMemory, ein einfacher boolescher Wert, der 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 benötigen mehr Arbeitsspeicher als andere. Sie können den Speicherverbrauch Ihrer App minimieren, indem Sie in Ihrem Code effizientere Alternativen auswählen.

Dienste sparsam nutzen

Wir empfehlen dringend, keine Dienste aktiv zu lassen, wenn sie unnötig sind. Unnötige Anhänge entfernen ausgeführte Dienste ist einer der größten Fehler bei der Speicherverwaltung, 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, bevorzugt das System, dass der Prozess für diesen Dienst weiterhin ausgeführt wird. Dieses erschwert Dienstprozesse, weil der von einem Dienst verwendete RAM für andere Prozesse nicht verfügbar. Dadurch wird die Anzahl der zwischengespeicherten Prozesse reduziert, die das System im LRU-Cache speichern, wodurch das Wechseln von Apps weniger effizient wird. Es kann sogar zu Rauschen im wenn der Arbeitsspeicher knapp wird und das System nicht genügend Prozesse zum Hosten aller Dienste verwalten kann. derzeit ausgeführt wird.

Vermeiden Sie im Allgemeinen die Verwendung persistenter Dienste aufgrund der anhaltenden Anforderungen an verfügbare Dienste zu speichern. Stattdessen empfehlen wir eine alternative Implementierung, z. B. WorkManager Weitere Informationen zur Planung von Hintergrundprozessen mit WorkManager, siehe 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, weil sie die Auswirkungen des Systems muss Autobox den Schlüssel und manchmal auch den Wert, wodurch ein oder zwei weitere Objekte pro Eintrag erstellt werden.

Bei Bedarf können Sie für eine schlanke Datenstruktur jederzeit zu Roharrays 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, erfordern 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-Protokollpuffer 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 verwenden Sie in Ihrem clientseitigen Code Protobufs immer auch 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 für die automatische Speicherbereinigung wirken sich nicht auf die Leistung Ihrer App aus. Viele automatische Speicherbereinigungen Kurzfristig auftretende Ereignisse können den Akku schnell entladen die Einrichtung von Frames aufgrund notwendiger Interaktionen zwischen der automatischen Speicherbereinigung und App-Threads. 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 Übung beschreibt die Speicherabwanderung die Anzahl der zugewiesenen temporären Objekte, die in einem bestimmten für einen bestimmten Zeitraum.

Sie können beispielsweise mehrere temporäre Objekte innerhalb einer for-Schleife zuweisen. Oder können Sie neue Paint erstellen oder Bitmap-Objekte innerhalb der onDraw() Funktion einer Ansicht. 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 dass die Speicherabwanderung 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 von Vorteil sind. Mit einem Objektpool Wenn eine Objektinstanz auf den Boden fällt, wird sie in einen Pool gelassen, wenn sie nicht mehr benötigt wird. Wenn Sie das nächste Mal eine Objektinstanz dieses Typs benötigen, können Sie sie aus dem Pool abrufen, als sie zuzuweisen.

Bewerten Sie die Leistung sorgfältig, um festzustellen, ob ein Objektpool in einer bestimmten Situation geeignet ist. Es gibt Fälle, in denen Objektpools die Leistung verschlechtern 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 als nötig zurückgehalten werden, wird auch der Papierkorb belastet. . Objektpools reduzieren zwar die Anzahl der Aufrufe der automatischen Speicherbereinigung, erhöht sich der Arbeitsaufwand für jeden Aufruf, da dies proportional zur Anzahl der (erreichbare) Livebyte.

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.

Gesamt-APK-Größe 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 Abhängigkeitsinjektion für Android, die auf Dagger ausgeführt wird. Dagger verwendet keine Reflexion, um die Code. Sie können die statische Kompilierungszeit-Implementierung von Dagger in Android-Apps verwenden, ohne Laufzeitkosten oder Arbeitsspeichernutzung.

Andere Abhängigkeitsinjektions-Frameworks, die Reflexion zum Initialisieren von Prozessen verwenden, für Annotationen. Dieser Vorgang kann deutlich mehr CPU-Zyklen und RAM erfordern und eine spürbare Verzögerung beim Start der App.

Vorsicht bei der Verwendung externer Bibliotheken

Externer Bibliothekscode wird oft 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 der Codegröße und des RAM-Speichers.

Selbst einige für Mobilgeräte optimierte Bibliotheken können aufgrund unterschiedlicher Implementierungen zu Problemen führen. Für Eine Bibliothek verwendet Lite-Protobufs, während eine andere Micro-Protobufs verwendet, was zu zwei protobuf-Implementierungen in Ihrer App verwendet. Dies kann bei verschiedenen Implementierungen Logging, Analysen, Frameworks für das Laden von Bildern, Caching und vieles mehr.

ProGuard kann dabei helfen, APIs und Ressourcen mit können die großen internen Abhängigkeiten einer Bibliothek nicht entfernt werden. Die Funktionen, die Sie erfordern diese Bibliotheken möglicherweise untergeordnete Abhängigkeiten. Dies wird besonders problematisch, wenn Sie die abgeleitete Activity-Klasse aus einem Bibliothek, die einen großen Bereich von Abhängigkeiten haben kann, wenn Bibliotheken die Reflexion verwenden, ist üblich und erfordert eine manuelle Feinabstimmung von ProGuard, damit es funktioniert.

Vermeiden Sie es, eine gemeinsam genutzte Bibliothek nur für ein oder zwei von Dutzenden Funktionen zu verwenden. Laden Sie keine großen Code und Aufwand, die Sie nicht verwenden. Wenn Sie sich für eine Bibliothek entscheiden, eine Implementierung, die genau Ihren Anforderungen entspricht. Andernfalls könnten Sie sich entscheiden, Ihre eigene Implementierung.