Wie viel RAM kann eine App maximal verwenden?

145

Ich bin sehr gespannt auf diese Frage zur Speicherverwaltung des Android-Betriebssystems und hoffe auf eine recht ausführliche Antwort zu diesem Thema.

Was ich gerne wissen würde:

  • Was ist die maximale Speichermenge (in Megabyte / als Prozentsatz des gesamten Arbeitsspeichers), die eine Android-Anwendung (die keine System-App ist) verwenden kann?
  • Gibt es Unterschiede zwischen Android-Versionen ?
  • Gibt es Unterschiede hinsichtlich des Herstellers des Geräts?

Und am wichtigsten:

  • Was wird berücksichtigt / wovon hängt es ab, wenn das System bestimmt, wie viel RAM eine App zur Laufzeit verwenden kann (vorausgesetzt, das maximale Speicher pro App ist keine statische Zahl)?

Was ich bisher gehört habe (bis 2013):

  • Frühe Android-Geräte hatten eine App-Obergrenze von 16 MB
  • Später erhöhte sich diese Obergrenze auf 24 MB oder 32 MB

Was mich sehr neugierig macht:

Beide Grenzwerte sind sehr niedrig.

Ich habe kürzlich den Android Task Manager heruntergeladen , um den RAM meines Geräts zu überprüfen. Was mir aufgefallen ist, ist, dass es Anwendungen gibt, die etwa 40-50 Megabyte RAM verwenden, was offensichtlich mehr ist als die erwähnte maximale RAM-Auslastung von beispielsweise 32 MB. Wie bestimmt Android, wie viel RAM eine App verwenden kann? Wie ist es möglich, dass Apps diese Grenze überschreiten?

Außerdem habe ich festgestellt, dass einige meiner Apps (vom System getötet?) Mit einer OutOfMemoryException abstürzen, wenn etwa 30-40 Megabyte verwendet werden. Auf der anderen Seite laufen auf meinem Telefon Apps mit 100 MB und mehr nach einiger Zeit (wahrscheinlich aufgrund von Speicherlecks), die nicht abstürzen oder abgetötet werden. Es hängt also natürlich auch von der App selbst ab , wie viel RAM gespart werden kann. Wie ist das möglich? (Ich habe meine Tests mit einem HTC One S mit 768 MB RAM durchgeführt.)

Haftungsausschluss: Ich bin in keiner Weise mit der Android Task Manager-App verbunden.

Philipp Jahoda
quelle

Antworten:

119

Was ist die maximale Speichermenge (in Megabyte / als Prozentsatz des gesamten Arbeitsspeichers), die eine Android-Anwendung (die keine System-App ist) verwenden kann?

Das variiert je nach Gerät. getMemoryClass()onActivityManager gibt Ihnen den Wert für das Gerät an, auf dem Ihr Code ausgeführt wird.

Gibt es Unterschiede zwischen den Android-Versionen?

Ja, sofern die Betriebssystemanforderungen im Laufe der Jahre gestiegen sind und die Geräte angepasst werden müssen.

Gibt es Unterschiede hinsichtlich des Herstellers des Geräts?

Ja, sofern Hersteller Geräte herstellen und die Größe je nach Gerät variiert.

Welche "Nebenfaktoren" werden bei der Bestimmung der RAM-Auslastung einer App berücksichtigt?

Ich habe keine Ahnung, was "Nebenfaktoren" bedeuten.

Frühe Geräte hatten eine Pro-App-Obergrenze von 16 MB. Spätere Geräte erhöhten dies auf 24 MB oder 32 MB

Das ist ungefähr richtig. Die Bildschirmauflösung ist eine wichtige Determinante, da größere Auflösungen größere Bitmaps bedeuten und Tablets und hochauflösende Telefone daher tendenziell noch höhere Werte aufweisen. Sie werden beispielsweise Geräte mit 48 MB Heaps sehen, und ich wäre nicht überrascht, wenn es höhere Werte gibt.

Wie ist es möglich, dass Apps diese Grenze überschreiten?

Sie gehen davon aus, dass der Autor dieser App weiß, was er tut. Angesichts der Tatsache, dass die Speichernutzung einer App für einen Android-Kerningenieur schwer zu bestimmen ist , würde ich nicht davon ausgehen, dass die betreffende App notwendigerweise besonders genaue Ergebnisse liefert.

Der native Code (NDK) unterliegt jedoch nicht dem Heap-Limit. Und seit Android 3.0 können Apps einen "großen Haufen" anfordern, normalerweise im Bereich von Hunderten von MB, aber das wird für die meisten Apps als schlechte Form angesehen.

Außerdem habe ich festgestellt, dass einige meiner Apps mit einer OutOfMemoryException abstürzen, wenn sie etwa 30-40 Megabyte verwenden.

Beachten Sie, dass der Android-Garbage Collector kein komprimierender Garbage Collector ist. Die Ausnahme sollte eigentlich sein CouldNotFindSufficientlyLargeBlockOfMemoryException, aber das wurde wahrscheinlich als zu wortreich angesehen. OutOfMemoryExceptionbedeutet, dass Sie Ihren angeforderten Block nicht zuordnen konnten , nicht, dass Sie Ihren Heap vollständig erschöpft haben.

CommonsWare
quelle
Ich kann nicht über Tabelle verstehen, ich habe Xperia X Mobile, die eine Auflösung von etwa 1080 x 1920 hat, was eine große Auflösung ist, und ein anderes Gerät Samsung Tab 4, das eine Auflösung von 800 x 1280 hat 3 GB RAM und Tab sind mit 1,5 GB RAM ausgestattet, sodass das Tablet aufgrund des großen Bildschirms einen großen RAM belegt.
Rahul Mandaliya
@ RahulMandaliya: Es tut mir leid, aber ich verstehe Ihr Anliegen nicht oder was es mit dieser Frage zu tun hat. Möglicherweise möchten Sie eine separate Frage zum Stapelüberlauf öffnen, in der Sie ausführlich erläutern, was Ihr Anliegen ist.
CommonsWare
15

Es ist Ende 2018, also haben sich die Dinge geändert.

Zunächst einmal: Führen Sie Ihre App aus und öffnen Sie die Registerkarte Android Profiler in Android Studio. Sie werden sehen, wie viel Speicher es verbraucht, Sie werden überrascht sein, aber es kann viel RAM zuweisen.

Außerdem finden Sie hier einen großartigen Artikel in offiziellen Dokumenten mit detaillierten Anweisungen zur Verwendung von Memory Profiler, mit denen Sie einen detaillierten Einblick in Ihre Speicherverwaltung erhalten.

In den meisten Fällen reicht Ihnen jedoch Ihr regulärer Android Profiler.

Geben Sie hier die Bildbeschreibung ein

Normalerweise startet eine App mit 50 MB RAM-Zuweisung, springt jedoch sofort auf 90 MB, wenn Sie einige Fotos in den Speicher laden. Wenn Sie Activity mit einem ViewPager mit vorinstallierten Fotos (jeweils 3,5 MB) öffnen, erhalten Sie in Sekundenschnelle 190 MB.

Dies bedeutet jedoch nicht, dass Sie Probleme mit der Speicherverwaltung haben.

Der beste Rat, den ich geben kann, ist, die Richtlinien und Best Practices zu befolgen, Top-Bibliotheken zum Laden von Bildern zu verwenden (Glide, Picasso) und alles in Ordnung zu sein.


Wenn Sie jedoch etwas anpassen müssen und wirklich wissen müssen, wie viel Speicher Sie manuell zuweisen können, können Sie den gesamten freien Speicher abrufen und einen festgelegten Anteil (in%) daraus berechnen. In meinem Fall musste ich entschlüsselte Fotos im Speicher zwischenspeichern, damit ich sie nicht jedes Mal entschlüsseln muss, wenn der Benutzer durch die Liste gleitet.

Zu diesem Zweck können Sie die gebrauchsfertige LruCache-Klasse verwenden . Es handelt sich um eine Cache-Klasse, die automatisch nachverfolgt, wie viel Speicher Ihre Objekte zuweisen (oder wie viele Instanzen), und die älteste entfernt, um die aktuelle anhand ihres Verwendungsverlaufs zu erhalten. Hier ist ein großartiges Tutorial zur Verwendung.

In meinem Fall habe ich zwei Instanzen von Caches erstellt: für Daumen und Anhänge. Sie wurden mit Singleton-Zugriff statisch gemacht, sodass sie global in der gesamten App verfügbar sind.

Cache-Klasse:

public class BitmapLruCache extends LruCache<Uri, byte[]> {

    private static final float CACHE_PART_FOR_THUMBS_PRC = 0.01f; // 1% (Nexus 5X - 5Mb)
    private static final float CACHE_PART_FOR_ATTACHMENTS_PRC = 0.03f;// 3% (Nexus 5X - 16Mb)
    private static BitmapLruCache thumbCacheInstance;
    private static BitmapLruCache attachmentCacheInstance;

public static synchronized BitmapLruCache getDecryptedThumbCacheInstance() {
    if (thumbCacheInstance == null) {

        int cacheSize = getCacheSize(CACHE_PART_FOR_THUMBS_PRC);
    //L.log("creating BitmapLruCache for Thumb with size: " + cacheSize + " bytes");
        thumbCacheInstance = new BitmapLruCache(cacheSize);
        return thumbCacheInstance;
    } else {
        return thumbCacheInstance;
    }
}

public static synchronized BitmapLruCache getDecryptedAttachmentCacheInstance() {
    if (attachmentCacheInstance == null) {

        int cacheSize = getCacheSize(CACHE_PART_FOR_ATTACHMENTS_PRC);
    //            L.log("creating BitmapLruCache for Attachment with size: " + cacheSize + " bytes");
        attachmentCacheInstance = new BitmapLruCache(cacheSize);
        return attachmentCacheInstance;
    } else {
        return attachmentCacheInstance;
    }
}

private BitmapLruCache(int maxSize) {
    super(maxSize);
}

public void addBitmap(Uri uri, byte[] bitmapBytes) {
    if (get(uri) == null && bitmapBytes != null)
        put(uri, bitmapBytes);
}

public byte[] getBitmap(Uri uri) {
    return get(uri);
}


@Override
protected int sizeOf(Uri uri, byte[] bitmapBytes) {
    // The cache size will be measured in bytes rather than number of items.
    return bitmapBytes.length;
}
}

So berechne ich den verfügbaren freien RAM und wie viel kann ich daraus herausbeißen:

private static int getCacheSize(float partOfTotalFreeMemoryToUseAsCache){
    final long maxMemory = Runtime.getRuntime().maxMemory();
    //Use ... of available memory for List Notes thumb cache
    return (int) (maxMemory * partOfTotalFreeMemoryToUseAsCache);
}

Und so verwende ich es in Adaptern, um zwischengespeicherte Bilder zu erhalten:

byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);

und wie ich es im Hintergrund-Thread in den Cache setze (reguläre AsyncTask):

BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes); 

Meine App zielt auf API 19+ ab, sodass Geräte nicht alt sind und diese Teile des verfügbaren Arbeitsspeichers in meinem Fall für den Cache ausreichen (1% und 3%).

Unterhaltsame Tatsache: Android verfügt über keine APIs oder andere Hacks, um die Ihrer App zugewiesene Speichermenge abzurufen. Sie wird im laufenden Betrieb anhand verschiedener Faktoren berechnet.


PS Ich verwende ein statisches Klassenfeld, um einen Cache zu speichern, aber gemäß den neuesten Android-Richtlinien wird empfohlen, die ViewModel-Architekturkomponente für diesen Zweck zu verwenden.

Kirill Karmazin
quelle