Hoher Speicherbedarf in Android - was tun?

Hallo liebe Leser.

Heute machen wir Sie auf ein wenig Material über den kompetenten Einsatz von Speicher in Android aufmerksam .



Viel Spaß beim Lesen!

Dieser Artikel konzentriert sich auf die grundlegenden Techniken zum Verwalten der Speichernutzung in Anwendungen - z. B. Browsern, Bildbearbeitungsprogrammen und PDF-Viewern -, in denen große Speicheranforderungen gestellt werden.

Zunächst eine kleine Theorie


Die meisten Android-Apps laufen auf der Laufzeit ( ART ), die die mittlerweile veraltete virtuelle Dalvik-Maschine ersetzt hat. ART und Dalvik ähneln der Java Virtual Machine (JVM), mit der sie ähnliche Entwurfsprinzipien teilen. Sie verwenden zwei separate Bereiche zum Speichern von Anwendungsdaten: den Stapel und den Heap.

Stapelspeicher Der

Stapelspeicher in Java wird zum Speichern lokaler Variablen (primitive Typen und Objektreferenzen) verwendet. Jeder Java-Thread hat einen eigenen Stapel. Der Stapelspeicher ist im Vergleich zum Heapspeicher relativ klein. Die Java-Stapelgröße von Dalvik beträgt normalerweise 32 KB für Java-Code und 1 MB für nativen Code (C ++ / JNI). In ART ist ein einheitlicher Stapel für Java und C ++ erschienen, dessen Größe etwa 1 MB beträgt.

Wenn die Anwendung den gesamten Stapelspeicher bis zum Limit auswählt, wird ein Fehler ausgegeben StackOverflowError. Die wahrscheinlichsten Gründe, warum ein Stapellimit erreicht werden kann, sind entweder eine unendliche Rekursion oder ein zu tiefer Methodenaufruf. Verweise auf den Stapelspeicher erfolgen immer in der LIFO-Reihenfolge (last come - first serve). Bei jedem Aufruf einer Methode wird ein neuer Frame mit den lokalen Variablen dieser Methode auf den Stapel verschoben. Wenn die Methode abgeschlossen ist, wird der Frame vom Stapel entfernt und ein möglicher resultierender Wert wird an den Stapel zurückgesendet. Das erste Problem (unendliche Rekursion) ist ein Fehler, der leicht zu beheben ist. Das zweite Problem erfordert jedoch ein Refactoring, das darin besteht, rekursive Methodenaufrufe bereitzustellen und in eine Schleife umzuwandeln.

Heapspeicher

Der Heap-Speicher in Java wird von einer virtuellen Maschine zum Zuweisen von Objekten verwendet. Immer wenn ein Objekt erstellt wird, geschieht dies auf dem Heap. Virtuelle Maschinen wie JVM oder ART sammeln regelmäßig Müll, entfernen alle Objekte, auf die nicht mehr verwiesen wird, und geben so Speicher frei, um neue Objekte zuzuweisen.
Um die Benutzerfreundlichkeit zu gewährleisten, begrenzt Android die Heap-Größen für jede laufende Anwendung streng. Die Beschränkung der Heap-Größe variiert von Gerät zu Gerät und hängt davon ab, wie viel RAM sich auf diesem Gerät befindet. Wenn Ihre Anwendung die maximale Heap-Größe erreicht und versucht, mehr Speicher zuzuweisen, wird ein Fehler generiert OutOfMemoryErrorund die Anwendung wird beendet. Schauen wir uns einige Beispiele an, um diese Situation zu vermeiden.

Heap-Speicheranalyse


Das wichtigste Werkzeug für das Verständnis Gedächtnisprobleme in Ihren Anwendungen und das Verständnis , wie Speicher verwendet wird , ist die Speicher - Profiler in Android Studio.

Dieses Tool visualisiert, wie viel Speicher Ihre Anwendung im Laufe der Zeit verbraucht. Sie können Snapshots des Java-Heaps in einer laufenden Anwendung erstellen, die Speicherzuweisungsvorgänge aufzeichnen und den Heap oder diesen Speicherzuweisungsverlauf in einer leistungsstarken Benutzeroberfläche verfolgen.

Eine typische Speicherprofilersitzung sollte folgendermaßen aussehen:

  • Wir untersuchen die häufigsten Speicherzuordnungen und Garbage Collector-Passagen, um mögliche Leistungsprobleme zu identifizieren.
  • , , , , , . , . , , PdfActivity PSPDFKit .
  • , . , . – , , .


Moderne Müllsammler sind komplexe Werke technologischer Kunst, das Ergebnis langjähriger Forschung und Entwicklung, an der Hunderte von Menschen teilgenommen haben, von Akademikern bis zu professionellen Entwicklern. Sie müssen jedoch weiterhin in Alarmbereitschaft sein, um Speicherverluste zu vermeiden.

Eine beispielhafte Lösung zum Erkennen von Speicherlecks ist die LeakCanary- Bibliothek . Es werden automatisch Benachrichtigungen ausgegeben, wenn Sie sich in Ihrer Testassembly (Entwicklungsversion) befinden, und Sie erhalten die Leckverfolgungsrate in der Benutzeroberfläche dieses Programms. Sie können (und sollten) es heute integrieren , zumal es nicht schwierig ist!

Es ist besonders einfach, Speicherlecks zu provozieren, wenn Sie mit komplexen Lebenszyklen von Aktivitäten oder Fragmenten von Android arbeiten. Dies geschieht häufig an Stellen, an denen Entwickler in der Hintergrundaufgabe oder in statischen Variablen starke Verweise auf UI- Kontexte oder andere UI-spezifische Objekte haben. Eine Möglichkeit, solche Verzögerungen zu provozieren, besteht darin, das Gerät beim Testen Ihrer Anwendung aktiv zu verdrehen.

Geben Sie als Reaktion auf Ereignisse Speicher frei


Android erfordert möglicherweise, dass die Anwendung Speicher zuweist, oder erzwingt einfach das Beenden, wenn der Speicher freigegeben werden muss, um kritischere Aufgaben auszuführen. Bevor dies geschieht, können Sie auf dem System den gesamten Speicher bereitstellen, den Sie nicht benötigen. In Ihrer Aktivität müssen Sie eine Schnittstelle implementieren ComponentCallbacks2. In diesem Fall wird Ihre Methode immer dann aufgerufen, wenn auf Ihrem System nicht genügend Arbeitsspeicher vorhanden ist onTrimMemory(), und Sie können Speicher freigeben oder Funktionen deaktivieren, die unter solchen Bedingungen mit Speichermangel nicht funktionieren.

Solche Rückrufe werden also in der PSPDFKit-Anwendung behandelt. Die PSPDFKit-Anwendung wurde mit der Berechnung der aktiven Speichernutzung für das Caching entwickelt, damit die Anwendung so reibungslos wie möglich ausgeführt wird. Zunächst ist nicht bekannt, wie viel Speicher auf dem Gerät verfügbar ist. Daher passt sich PSPDFKit der Situation an und beschränkt die Verwendung des Speichers, wenn Benachrichtigungen empfangen werden, dass nicht genügend Speicher vorhanden ist. Daher funktionieren in PSPDFKit integrierte Anwendungen auch auf Low-Tech-Geräten, jedoch mit reduzierter Leistung, da das Caching deaktiviert ist.

Großer Haufen


Eine der Front-End-Lösungen zur Bewältigung des hohen Speicherbedarfs besteht darin, eine große Menge Dalvik für Ihre Anwendung anzufordern. Dazu können Sie android:largeHeap="true"dem <application> -Tag in der Datei hinzufügen AndroidManifest.xml.

Wenn die Eigenschaft largeHeapauf value festgelegt ist true, erstellt Android alle Prozesse für Ihre Anwendung mit einem großen Heap. Diese Einstellung ist nur für Anwendungen vorgesehen, die naturgemäß ohne sie nicht funktionieren können, dh sie verwenden umfangreiche Ressourcen, die gleichzeitig in den Speicher passen müssen.

Es wird dringend davon abgeraten, einen großen Heap zu verwenden, wenn Sie nur die Obergrenze für eine mögliche Speichernutzung erhöhen möchten. Die Speichernutzung sollte immer optimiert werden, da selbst ein großer Stapel Ihrer Anwendung möglicherweise nicht ausreicht, wenn Sie an einem schwachen Gerät mit kleinem Speicher arbeiten.

Überprüfen Sie, wie viel Speicher Ihre Anwendung verwenden kann


Es schadet nie zu überprüfen, wie groß der Heap Ihrer Anwendung ist, und Ihren Code und die verfügbaren Funktionen dynamisch an diese Speichergrenzen anzupassen. Sie können die maximale Heap-Größe direkt zur Laufzeit mithilfe von Methoden getMemoryClass()oder getLargeMemoryClass()(wenn ein großer Heap aktiviert ist) überprüfen .

Android unterstützt sogar Geräte mit nur 512 MB RAM. Ignorieren Sie Low-Tech-Geräte nicht! Mit der MethodeisLowRamDevice()Sie können überprüfen, ob Ihre Anwendung auf einem solchen Gerät ausgeführt wird, auf dem nicht genügend Speicher verfügbar ist. Das genaue Verhalten dieser Methode ist geräteabhängig, gibt jedoch normalerweise auf Geräten mit weniger als 1 GB RAM true zurück. Sie müssen sicherstellen, dass Ihre Anwendung auf diesen Geräten ordnungsgemäß funktioniert, und alle Funktionen deaktivieren, die viel Speicherplatz auf ihnen verwenden.

Lesen Sie mehr darüber , wie Android funktioniert auf Geräten mit einer geringen Menge an Speicher, können Sie lesen hier ; Weitere Optimierungstipps finden Sie hier.

Verwenden Sie optimierte Datenstrukturen


In vielen Fällen verwenden Anwendungen zu viel Speicher, weil sie nicht die am besten geeigneten Datenstrukturen verwenden.

Java-Sammlungen können keine effizienten primitiven Typen speichern und erfordern das Packen ihrer Schlüssel und Werte. Beispielsweise sollten HashMapmit ganzzahligen Schlüsseln durch optimierte ersetzt werden SparseArray. Letztendlich können Sie immer Raw-Arrays anstelle von Sammlungen verwenden. Dies ist eine gute Idee, wenn die Größe Ihrer Sammlung nicht geändert werden kann.

Andere Datenstrukturen, die hinsichtlich der Speichernutzung ineffizient sind, umfassen verschiedene Serialisierungen. Ja, XML- oder JSON-Formate sind in der Tat bequem zu verwenden. Sie können die Speichernutzung reduzieren, wenn Sie mit einem effizienteren Binärformat arbeiten, z. B. Protokollpuffern.

Alle diese Beispiele mit Datenstrukturen, die zum Speichern von Speicher optimiert wurden, sind nur Hinweise. Wie beim Refactoring müssen Sie zuerst die Ursache der Probleme finden und dann mit solchen Leistungsoptimierungen fortfahren.

Verhindern Sie das Mischen des Speichers


Virtuelle Java / Android-Maschinen weisen Objekte sehr schnell zu. Die Speicherbereinigung ist auch sehr schnell. Wenn Sie jedoch in kurzer Zeit eine große Anzahl von Objekten zuweisen, kann ein Problem auftreten, das als "Speicherabwanderung" bezeichnet wird. In diesem Fall hat die virtuelle Maschine keine Zeit, Objekte in diesem Tempo zuzuweisen, und der Garbage Collector entsorgt sie, und die Anwendung wird langsamer und verbraucht in extremen Fällen sogar den gesamten Speicher.

Das Hauptproblem auf Android-Gebiet in diesem Fall ist, dass wir nicht steuern, wann die Speicherbereinigung stattfinden wird. Dies kann möglicherweise zu Problemen führen: Beispielsweise arbeitet der Garbage Collector genau zu dem Zeitpunkt, zu dem sich die Animation auf dem Bildschirm entfaltet, und wir überschreiten den Schwellenwert von 16 ms, wodurch eine reibungslose Anzeige der Frames gewährleistet wird. Daher ist es wichtig, eine Überbelegung des Speichers im Code zu verhindern.

Ein Beispiel für eine Situation, die zu einer Speicherumwandlung führt, ist die Zuweisung großer Objekte, z. B. Malen innerhalb der onDraw()Präsentationsmethode. In diesem Fall werden viele Objekte schnell erstellt, und die Speicherbereinigung kann beginnen, was sich negativ auf die Leistung dieser Ansicht auswirken kann. Wie oben erwähnt, sollten Sie die Speichernutzung immer überwachen, um solche Situationen zu vermeiden.

Fazit


Arbeitsspeicher (RAM) auf Mobilgeräten kann eine sehr begrenzte Ressource sein. Die Gewährleistung einer effizienten Speichernutzung in der Anwendung ist besonders wichtig, wenn Ihre Anwendung mit relativ großen Objekten arbeitet, z. B. Rastergrafiken (PDF-Viewer, Webbrowser, Bildbearbeitungsprogramme) oder großen Mediendateien (Audio- oder Videobearbeitungsprogramme). Wenn Sie diese Tipps befolgen, erfahren Sie, wie Sie hochwertige Anwendungen erstellen, die selbst auf den leistungsstärksten Geräten auf einem akzeptablen Niveau funktionieren.

All Articles