Ermitteln Sie die Größe des Anwendungsheaps in Android

145

Wie erkennt man programmgesteuert die Größe des Anwendungsheaps, die für eine Android-App verfügbar ist?

Ich habe gehört, dass es eine Funktion gibt, die dies in späteren Versionen des SDK tut. Auf jeden Fall suche ich nach einer Lösung, die für 1.5 und höher funktioniert.

hpique
quelle
2
Siehe auch
Josh Lee

Antworten:

453

Es gibt zwei Möglichkeiten, über Ihren Ausdruck "Größe des Anwendungsheaps verfügbar" nachzudenken:

  1. Wie viel Heap kann meine App verwenden, bevor ein schwerer Fehler ausgelöst wird? Und

  2. Wie viel Heap sollte meine App angesichts der Einschränkungen der Android-Betriebssystemversion und der Hardware des Geräts des Benutzers verwenden?

Es gibt eine andere Methode zur Bestimmung der oben genannten.

Für Punkt 1 oben: maxMemory()

Dies kann onCreate()wie folgt aufgerufen werden (z. B. in der Methode Ihrer Hauptaktivität ):

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

Diese Methode gibt an, wie viele Heap- Bytes Ihre App insgesamt verwenden darf .

Für Punkt 2 oben: getMemoryClass()

die wie folgt aufgerufen werden kann:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

Diese Methode gibt ungefähr an, wie viele Megabyte Heap Ihre App verwenden sollte , wenn die Grenzen des aktuellen Geräts und die Rechte anderer Apps zur Ausführung beachtet werden sollen, ohne wiederholt in den onStop()/ onResume()-Zyklus gezwungen zu werden, da diese grob sind Der Speicher wird gelöscht, während Ihre Elefanten-App im Android-Whirlpool badet.

Diese Unterscheidung ist meines Wissens nicht eindeutig dokumentiert, aber ich habe diese Hypothese auf fünf verschiedenen Android-Geräten getestet (siehe unten) und zu meiner eigenen Zufriedenheit bestätigt, dass dies eine korrekte Interpretation ist.

Bei einer Standardversion von Android maxMemory()wird normalerweise ungefähr die gleiche Anzahl von Megabyte zurückgegeben, wie in angegeben getMemoryClass()(dh ungefähr eine Million Mal der letztere Wert).

Die einzige Situation (von der ich weiß), in der die beiden Methoden voneinander abweichen können, ist ein gerootetes Gerät, auf dem eine Android-Version wie CyanogenMod ausgeführt wird. Auf diese Weise kann der Benutzer manuell auswählen, wie groß ein Heap für jede App sein soll. In CM wird diese Option beispielsweise unter "CyanogenMod-Einstellungen" / "Leistung" / "VM-Heap-Größe" angezeigt.

HINWEIS: BEACHTEN SIE, DASS DIE MANUELLE EINSTELLUNG DIESES WERTES IHR SYSTEM MESSEN KANN, INSBESONDERE, wenn Sie einen kleineren Wert als für Ihr Gerät normal auswählen.

Hier sind meine Testergebnisse, die die von maxMemory()und getMemoryClass()für vier verschiedene Geräte mit CyanogenMod zurückgegebenen Werte zeigen , wobei jeweils zwei verschiedene (manuell eingestellte) Heap-Werte verwendet werden:

  • G1:
    • Bei einer VM-Heap-Größe von 16 MB:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Bei einer VM-Heap-Größe von 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Bei einer VM-Heap-Größe von 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Bei einer VM-Heap-Größe von 16 MB:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus Eins:
    • Bei einer VM-Heap-Größe von 32 MB:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Bei einer VM-Heap-Größe von 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewsonic GTab:
    • Bei VM-Heap-Größe auf 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Mit VM Heap Size auf 64 eingestellt:
      • maxMemory: 67108864
      • getMemoryClass: 32

Darüber hinaus habe ich auf einem Novo7 Paladin-Tablet mit Ice Cream Sandwich getestet. Dies war im Wesentlichen eine Standardversion von ICS, mit der Ausnahme, dass ich das Tablet durch einen einfachen Prozess gerootet habe, der nicht das gesamte Betriebssystem ersetzt und insbesondere keine Schnittstelle bietet, über die die Heap-Größe manuell angepasst werden kann.

Für dieses Gerät sind hier die Ergebnisse:

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

Auch (per Kishore in einem Kommentar unten):

  • HTC ein x
    • maxMemory: 67108864
    • getMemoryClass: 64

Und (laut Akauppis Kommentar):

  • Samsung Galaxy Core Plus
    • maxMemory: (Nicht im Kommentar angegeben)
    • getMemoryClass: 48
    • largeMemoryClass: 128

Per Kommentar von cmcromance:

  • Galaxy S3 (Jelly Bean) großer Haufen
    • maxMemory: 268435456
    • getMemoryClass: 64

Und (Kommentare von Tencent):

  • LG Nexus 5 (4.4.3) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • LG Nexus 5 (4.4.3) großer Haufen
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) normal
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) großer Haufen
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Galaxy S4 Play Store Edition (4.4.2) großer Haufen
    • maxMemory: 536870912
    • getMemoryClass: 192

Andere Geräte

  • Huawei Nexus 6P (6.0.1) normal
    • maxMemory: 201326592
    • getMemoryClass: 192

Ich habe diese beiden Methoden nicht mit dem speziellen Android getestet: largeHeap = "true" Manifestoption, die seit Honeycomb verfügbar ist, aber dank cmcromance und tencent haben wir einige Beispielwerte für largeHeap, wie oben angegeben.

Meine Erwartung (die durch die oben genannten LargeHeap-Zahlen unterstützt zu werden scheint) wäre, dass diese Option einen ähnlichen Effekt hat wie das manuelle Festlegen des Heaps über ein verwurzeltes Betriebssystem - dh den Wert von erhöht, maxMemory()während Sie in getMemoryClass()Ruhe lassen. Es gibt eine andere Methode, getLargeMemoryClass (), die angibt, wie viel Speicher für eine App mit der Einstellung largeHeap zulässig ist. In der Dokumentation zu getLargeMemoryClass () heißt es: "Die meisten Anwendungen sollten nicht so viel Speicher benötigen und stattdessen das Limit getMemoryClass () einhalten."

Wenn ich richtig geraten habe, hätte die Verwendung dieser Option dieselben Vorteile (und Gefahren) wie die Verwendung des Speicherplatzes, der von einem Benutzer bereitgestellt wird, der den Heap über ein verwurzeltes Betriebssystem erhöht hat (dh wenn Ihre App den zusätzlichen Speicher verwendet). Es wird wahrscheinlich nicht so gut mit allen anderen Apps gespielt, die der Benutzer gleichzeitig ausführt.

Beachten Sie, dass die Speicherklasse anscheinend kein Vielfaches von 8 MB sein muss.

Aus dem getMemoryClass()Obigen können wir ersehen, dass das Ergebnis für eine bestimmte Geräte- / Betriebssystemkonfiguration unverändert bleibt, während sich der Wert für maxMemory () ändert, wenn der Heap vom Benutzer anders festgelegt wird.

Meine eigene praktische Erfahrung ist, dass ich auf dem G1 (mit einer Speicherklasse von 16), wenn ich manuell 24 MB als Heap-Größe auswähle, ohne Fehler ausgeführt werden kann, selbst wenn meine Speichernutzung auf 20 MB ansteigen darf (vermutlich könnte dies der Fall sein) bis zu 24 MB hoch gehen, obwohl ich das noch nicht ausprobiert habe). Aber andere ähnlich große Apps werden möglicherweise aufgrund der Schweinerei meiner eigenen App aus dem Speicher gelöscht. Umgekehrt wird meine App möglicherweise aus dem Speicher gelöscht, wenn diese anderen wartungsintensiven Apps vom Benutzer in den Vordergrund gerückt werden.

Sie können also nicht die von angegebene Speichermenge überschreiten maxMemory(). Und Sie sollten versuchen , innerhalb der von angegebenen Grenzen zu bleiben getMemoryClass(). Eine Möglichkeit, dies zu tun, besteht darin, die Funktionalität solcher Geräte so einzuschränken, dass Speicherplatz gespart wird, wenn alles andere fehlschlägt.

Wenn Sie vorhaben, die in angegebene Anzahl von Megabyte zu überschreiten getMemoryClass(), würde ich Ihnen raten, lange und intensiv am Speichern und Wiederherstellen des Status Ihrer App zu arbeiten, damit die Benutzererfahrung praktisch nicht unterbrochen wird, wenn ein onStop()/ onResume()Zyklus auftritt.

In meinem Fall beschränke ich meine App aus Leistungsgründen auf Geräte mit 2.2 und höher. Dies bedeutet, dass fast alle Geräte, auf denen meine App ausgeführt wird, eine Speicherklasse von 24 oder höher haben. So kann ich bis zu 20 MB Heap belegen und bin mir ziemlich sicher, dass meine App mit den anderen Apps, die der Benutzer möglicherweise gleichzeitig ausführt, gut funktioniert.

Es wird jedoch immer einige Root-Benutzer geben, die eine Android-Version 2.2 oder höher auf ein älteres Gerät (z. B. ein G1) geladen haben. Wenn Sie eine solche Konfiguration auftreten, im Idealfall sollten Sie Ihre Speichernutzung abspecken, auch wenn maxMemory()Ihnen sagt , dass Sie viel höher als die 16MB gehen können , die getMemoryClass()Ihnen sagt , dass Sie sollten Targeting werden. Und wenn Sie nicht zuverlässig sicherstellen können, dass Ihre App innerhalb dieses Budgets lebt, stellen Sie zumindest sicher, dass onStop()/ onResume()nahtlos funktioniert.

getMemoryClass()Wie von Diane Hackborn (hackbod) oben angegeben, ist sie nur ab API-Level 5 (Android 2.0) verfügbar. Sie können daher davon ausgehen, dass die physische Hardware aller Geräte, auf denen eine frühere Version des Betriebssystems ausgeführt wird, ausgelegt ist um Apps optimal zu unterstützen, die einen Heap-Speicherplatz von nicht mehr als 16 MB belegen.

Im Gegensatz dazu maxMemory()wird gemäß der Dokumentation, die ganzen Weg zurück auf API - Ebene 1 zur Verfügung steht maxMemory(), auf einer Pre-2.0 - Version wird wahrscheinlich gibt einen 16MB Wert, aber ich tue , dass in meinen (viel später) CyanogenMod Versionen der Benutzer Sie können einen Heap-Wert von nur 12 MB auswählen, was vermutlich zu einer niedrigeren Heap-Grenze führen würde. Daher würde ich vorschlagen, dass Sie den maxMemory()Wert auch für Versionen des Betriebssystems vor 2.0 weiter testen . In dem unwahrscheinlichen Fall, dass dieser Wert sogar auf weniger als 16 MB festgelegt ist, müssen Sie möglicherweise sogar die Ausführung verweigern, wenn Sie mehr als maxMemory()angegeben benötigen .

Carl
quelle
2
@ Carl Hallo Carl, danke für deinen tollen Beitrag. Und ich habe die Option Android getestet: largeHeap = "true" auf dem Galaxy S3 mit JellyBean. Hier ist das Ergebnis. [maxMemory: 256,0 MB, Speicherklasse: 64 MB] (maxMemory war 64 MB ohne die Option "Largeheap")
cmcromance
1
@ Carl Haha Es ist eine große Ehre! :)
cmcromance
1
Für HTC One X: maxMemory: 67108864 memoryClass: 64
Kishore
1
LG Nexus 5 (4.4.3) (normal): maxMemory: 201326592, memoryClass: 192 | LG Nexus 5 (4.4.3) (großer Haufen): maxMemory: 536870912, Speicherklasse: 192
ian.shaun.thomas
1
Sehr schön dokumentiert. Bitte geben Sie dies für Android. Ich kann kein passendes Android-Dokument finden
Jimit Patel
20

Die offizielle API lautet:

Dies wurde in 2.0 eingeführt, wo größere Speichergeräte erschienen. Sie können davon ausgehen, dass Geräte, auf denen frühere Versionen des Betriebssystems ausgeführt werden, die ursprüngliche Speicherklasse verwenden (16).

Hackbod
quelle
13

Debug.getNativeHeapSize()werde den Trick machen, sollte ich denken. Es ist jedoch seit 1.0 da.

Die DebugKlasse verfügt über viele großartige Methoden zum Verfolgen von Zuordnungen und anderen Leistungsproblemen. Wenn Sie eine Situation mit wenig Arbeitsspeicher feststellen müssen, überprüfen Sie dies Activity.onLowMemory().

Neil Traft
quelle
Danke Neil. Funktioniert dies, wenn die Anwendung mit deaktiviertem Debugging ausgeführt wird?
Hpique
2
Ich bin ziemlich neu in diesem Bereich, aber ich glaube nicht, dass der native Heap mit dem App-Heap (Dalvik) identisch ist, auf den sich die Frage bezieht. Der native Heap bietet Unterstützung für Bitmap-Daten, die durch nativen Code zugewiesen werden, während der App-Heap Java-Anwendungsdaten enthält. Ich war interessiert zu erfahren, dass der native Heap auf das Limit des App-Heaps angerechnet wird und nach 3.0 die Zuordnungen tatsächlich auf dem App-Heap erfolgen. Diane Hackborn (hackbod) schreibt hier zu diesem Thema: stackoverflow.com/questions/1945142/bitmaps-in-android Aber selbst für 3.0 gibt es auch nicht "native" Daten auf dem App-Heap.
Carl
1
Seit einigen aktuellen Android-Updates (möglicherweise KitKat 4.4) befinden sich die Bitmaps im JVM-Heap.
Akauppi
13

So geht's:

Ermitteln der maximalen Heap-Größe, die die App verwenden kann:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

Abrufen, wie viel von dem Heap Ihre App derzeit verwendet:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

Abrufen, wie viel von dem Heap Ihre App jetzt verwenden kann (verfügbarer Speicher):

long availableMemory=maxMemory-usedMemory;

Und um jedes von ihnen schön zu formatieren, können Sie Folgendes verwenden:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 
Android-Entwickler
quelle
5

Dies gibt die maximale Heap-Größe in Bytes zurück:

Runtime.getRuntime().maxMemory()

Ich habe ActivityManager.getMemoryClass () verwendet, aber auf CyanogenMod 7 (ich habe es nicht an anderer Stelle getestet) wird ein falscher Wert zurückgegeben, wenn der Benutzer die Heap-Größe manuell festlegt.

fhucho
quelle
Da das Dokument für getMemoryClasszu implizieren scheint, dass die Anzahl möglicherweise nicht mit der verfügbaren Heap-Größe für Ihr VM übereinstimmt, und das Dokument für getNativeHeapSize... stillschweigend ist, denke ich wirklich, dass dies Runtime.getRuntime().maxMemory()die beste Antwort ist.
BoD
3

Einige Vorgänge sind schneller als Java Heap Space Manager. Durch das Verzögern von Vorgängen für einige Zeit kann Speicherplatz frei werden. Mit dieser Methode können Sie Fehler in der Heap-Größe vermeiden:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * space.
 *
 * @param callback Callback operations to be done when memory is free.
 */
public static void waitForGarbageCollector(final Runnable callback) {

  Runtime runtime;
  long maxMemory;
  long usedMemory;
  double availableMemoryPercentage = 1.0;
  final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
  final int DELAY_TIME = 5 * 1000;

  runtime =
    Runtime.getRuntime();

  maxMemory =
    runtime.maxMemory();

  usedMemory =
    runtime.totalMemory() -
    runtime.freeMemory();

  availableMemoryPercentage =
    1 -
    (double) usedMemory /
    maxMemory;

  if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {

    try {
      Thread.sleep(DELAY_TIME);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    waitForGarbageCollector(
      callback);
  } else {

    // Memory resources are availavle, go to next operation:

    callback.run();
  }
}
Zon
quelle
Gut getestet. Können Sie diese beiden Dinge näher erläutern: availableMemoryPercentage und MIN_AVAILABLE_MEMORY_PERCENTAGE? einige Erklärungen ?
Noor Hossain
1
AvailableMemoryPercentageist nach Formel: Wie viel Speicher des Geräts ist derzeit frei. MIN_AVAILABLE_MEMORY_PERCENTAGEist Ihr benutzerdefinierter Parameter, ein Schwellenwert, ab dem Sie darauf warten, dass der Garbage Collector seine Aufgabe erfüllt.
Zon
1

Asus Nexus 7 (2013) 32Gig: getMemoryClass () = 192 maxMemory () = 201326592

Ich habe den Fehler gemacht, mein Spiel auf dem Nexus 7 als Prototyp zu erstellen, und dann festgestellt, dass auf dem generischen 4.04-Tablet meiner Frau (Speicherklasse 48, maxmemory 50331648) fast sofort nicht mehr genügend Speicher vorhanden ist.

Ich muss mein Projekt umstrukturieren, um weniger Ressourcen zu laden, wenn ich feststelle, dass die Speicherklasse niedrig ist.
Gibt es in Java eine Möglichkeit, die aktuelle Heap-Größe anzuzeigen? (Ich kann es beim Debuggen deutlich in der logCat sehen, aber ich möchte eine Möglichkeit, es im Code zu sehen, um es anzupassen, z. B. wenn currentheap> (maxmemory / 2) hochwertige Bitmaps entlädt und niedrige Qualität lädt

Tkraindesigns
quelle
Auf besagtem Nexus7-2013, getLargeMemoryClass () = 512.
akauppi
0

Meinen Sie programmatisch oder nur während Sie entwickeln und debuggen? In letzterem Fall können Sie diese Informationen aus der DDMS-Perspektive in Eclipse anzeigen. Wenn Ihr Emulator (möglicherweise sogar ein angeschlossenes physisches Telefon) ausgeführt wird, werden die aktiven Prozesse in einem Fenster auf der linken Seite aufgelistet. Sie können es auswählen und es gibt eine Option zum Verfolgen der Heap-Zuordnungen.

Steve Haley
quelle
Ich meine programmatisch. Klärte die Frage. Vielen Dank.
Hpique
1
Ich schlage vor, Sie sehen sich diesen Beitrag von einem der Android-Plattform-Programmierer an: stackoverflow.com/questions/2298208/…
Steve Haley
0
Runtime rt = Runtime.getRuntime();
rt.maxMemory()

Wert ist b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

Wert ist MB

Qinqie
quelle
Welche Art von rt? Wo wird es deklariert?
FindOut_Quran