Wie kann ich den von meiner Android-Anwendung verwendeten Speicher programmgesteuert ermitteln?
Ich hoffe, es gibt eine Möglichkeit, dies zu tun. Außerdem, wie bekomme ich den freien Speicher des Telefons zu?
Wie kann ich den von meiner Android-Anwendung verwendeten Speicher programmgesteuert ermitteln?
Ich hoffe, es gibt eine Möglichkeit, dies zu tun. Außerdem, wie bekomme ich den freien Speicher des Telefons zu?
Beachten Sie, dass die Speichernutzung auf modernen Betriebssystemen wie Linux ein extrem komplizierten und schwer zu verstehenden Bereich. Die Wahrscheinlichkeit, dass Sie die Zahlen, die Sie erhalten, tatsächlich richtig interpretieren, ist äußerst gering. (So gut wie jedes Mal, wenn ich mir mit anderen Ingenieuren Zahlen zur Speichernutzung ansehe, gibt es eine lange Diskussion darüber, was sie eigentlich bedeuten, die nur zu einer vagen Schlussfolgerung führt).
Hinweis: Wir haben jetzt eine viel umfangreichere Dokumentation über Verwalten des Speichers Ihrer App das einen Großteil des hier behandelten Themas abdeckt und auf dem neuesten Stand der Android-Entwicklung ist.
Als erstes sollten Sie den letzten Teil dieses Artikels lesen, in dem es um die Speicherverwaltung unter Android geht:
Dienst-API-Änderungen ab Android 2.0
Jetzt ActivityManager.getMemoryInfo()
ist unsere API auf höchster Ebene, um die gesamte Speichernutzung zu betrachten. Sie dient vor allem dazu, einer Anwendung dabei zu helfen, abzuschätzen, wie nahe das System daran ist, keinen Speicher mehr für Hintergrundprozesse zu haben, so dass es notwendig wird, benötigte Prozesse wie Dienste zu beenden. Für reine Java-Anwendungen sollte dies von geringem Nutzen sein, da das Java-Heap-Limit zum Teil dazu dient, zu verhindern, dass eine Anwendung das System bis zu diesem Punkt belasten kann.
Auf einer niedrigeren Ebene können Sie die Debug-API verwenden, um Rohinformationen über die Speichernutzung auf Kernel-Ebene zu erhalten: Android.os.Debug.MemoryInfo
Ab Version 2.0 gibt es auch eine API, ActivityManager.getProcessMemoryInfo
um diese Informationen über einen anderen Prozess zu erhalten: ActivityManager.getProcessMemoryInfo(int[])
Dies gibt eine Low-Level MemoryInfo-Struktur mit all diesen Daten zurück:
/** The proportional set size for dalvik. */
public int dalvikPss;
/** The private dirty pages used by dalvik. */
public int dalvikPrivateDirty;
/** The shared dirty pages used by dalvik. */
public int dalvikSharedDirty;
/** The proportional set size for the native heap. */
public int nativePss;
/** The private dirty pages used by the native heap. */
public int nativePrivateDirty;
/** The shared dirty pages used by the native heap. */
public int nativeSharedDirty;
/** The proportional set size for everything else. */
public int otherPss;
/** The private dirty pages used by everything else. */
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
Aber was ist der Unterschied zwischen Pss
, PrivateDirty
et SharedDirty
... und jetzt beginnt der Spaß.
Ein Großteil des Speichers in Android (und Linux-Systemen im Allgemeinen) wird von mehreren Prozessen gemeinsam genutzt. Wie viel Speicher ein Prozess verbraucht, ist also nicht wirklich klar. Wenn dann noch das Auslagern auf die Festplatte hinzukommt (ganz zu schweigen vom Swap, den wir bei Android nicht verwenden), wird es noch unklarer.
Würde man also den gesamten physischen Arbeitsspeicher nehmen, der tatsächlich jedem Prozess zugeordnet ist, und alle Prozesse zusammenzählen, käme man wahrscheinlich auf eine Zahl, die viel größer ist als der tatsächliche Gesamtspeicher.
El Pss
Zahl ist eine Metrik, die der Kernel berechnet und die die gemeinsame Nutzung des Speichers berücksichtigt - im Grunde wird jede Seite des Arbeitsspeichers in einem Prozess durch ein Verhältnis der Anzahl der anderen Prozesse, die diese Seite ebenfalls nutzen, skaliert. Auf diese Weise können Sie (theoretisch) den pss-Wert aller Prozesse addieren, um den gesamten von ihnen verwendeten RAM-Speicher zu sehen, und den pss-Wert zwischen den Prozessen vergleichen, um eine ungefähre Vorstellung von ihrem relativen Gewicht zu erhalten.
Die andere interessante Kennzahl ist PrivateDirty
Dies ist im Grunde die Menge an RAM innerhalb des Prozesses, die nicht auf die Festplatte ausgelagert werden kann (sie wird nicht durch dieselben Daten auf der Festplatte gesichert) und nicht mit anderen Prozessen geteilt wird. Eine andere Betrachtungsweise ist die des Arbeitsspeichers, der dem System zur Verfügung steht, wenn der Prozess verschwindet (und wahrscheinlich schnell in den Cache und andere Verwendungen übergeht).
Das sind im Wesentlichen die SDK-APIs für diesen Bereich. Es gibt jedoch noch mehr, was Sie als Entwickler mit Ihrem Gerät tun können.
Verwendung von adb
gibt es eine Reihe von Informationen über die Speichernutzung eines laufenden Systems. Eine gängige Methode ist der Befehl adb shell dumpsys meminfo
das eine Reihe von Informationen über den Speicherverbrauch jedes Java-Prozesses ausspuckt, die die oben genannten Informationen sowie eine Vielzahl anderer Dinge enthalten. Sie können auch den Namen oder die PID eines einzelnen Prozesses eingeben, um z.B. zu sehen adb shell dumpsys meminfo system
Geben Sie mir den Systemprozess:
\*\* MEMINFO in pid 890 \[system\] \*\*
native dalvik other total
size: 10940 7047 N/A 17987
allocated: 8943 5516 N/A 14459
free: 336 1531 N/A 1867
(Pss): 4585 9282 11916 25783
(shared dirty): 2184 3596 916 6696
(priv dirty): 4504 5956 7456 17916
Objects
Views: 149 ViewRoots: 4
AppContexts: 13 Activities: 0
Assets: 4 AssetManagers: 4
Local Binders: 141 Proxy Binders: 158
Death Recipients: 49
OpenSSL Sockets: 0
SQL
heap: 205 dbFiles: 0
numPagers: 0 inactivePageKB: 0
activePageKB: 0
Der obere Abschnitt ist der wichtigste, in dem size
ist die Gesamtgröße des Adressraums eines bestimmten Heaps, allocated
ist die Anzahl der KB an tatsächlichen Zuweisungen, die der Heap zu haben glaubt, free
die verbleibenden KB, die der Heap für zusätzliche Zuweisungen frei hat, und pss
y priv dirty
sind die gleichen wie die zuvor besprochenen, spezifisch für die mit jedem der Heaps verbundenen Seiten.
Wenn Sie sich nur die Speichernutzung aller Prozesse ansehen wollen, können Sie den Befehl adb shell procrank
. Die Ausgabe auf demselben System sieht wie folgt aus:
PID Vss Rss Pss Uss cmdline
890 84456K 48668K 25850K 21284K system\_server
1231 50748K 39088K 17587K 13792K com.android.launcher2
947 34488K 28528K 10834K 9308K com.android.wallpaper
987 26964K 26956K 8751K 7308K com.google.process.gapps
954 24300K 24296K 6249K 4824K com.android.phone
948 23020K 23016K 5864K 4748K com.android.inputmethod.latin
888 25728K 25724K 5774K 3668K zygote
977 24100K 24096K 5667K 4340K android.process.acore
...
59 336K 332K 99K 92K /system/bin/installd
60 396K 392K 93K 84K /system/bin/keystore
51 280K 276K 74K 68K /system/bin/servicemanager
54 256K 252K 69K 64K /system/bin/debuggerd
Hier wird die Vss
y Rss
Spalten sind im Grunde Rauschen (es handelt sich dabei um den reinen Adressraum- und RAM-Verbrauch eines Prozesses, bei dem man, wenn man den RAM-Verbrauch aller Prozesse zusammenzählt, eine lächerlich große Zahl erhält).
Pss
ist, wie wir bereits gesehen haben, und Uss
es Priv Dirty
.
Dies ist eine interessante Feststellung: Pss
y Uss
unterscheiden sich leicht (oder mehr als leicht) von dem, was wir in meminfo
. Warum ist das so? Nun, procrank verwendet einen anderen Kernel-Mechanismus zum Sammeln seiner Daten als meminfo
und sie liefern leicht unterschiedliche Ergebnisse. Warum ist das so? Ehrlich gesagt, habe ich keine Ahnung. Ich glaube procrank
ist vielleicht der genauere... aber eigentlich geht es hier nur darum: "Nehmen Sie jede Gedächtnisinformation, die Sie bekommen, mit einem Körnchen Salz, oft einem sehr großen Körnchen."
Schließlich gibt es noch den Befehl adb shell cat /proc/meminfo
die einen Überblick über die gesamte Speichernutzung des Systems gibt. Es gibt hier eine Menge Daten, von denen nur die ersten paar Zahlen diskussionswürdig sind (und die restlichen nur von wenigen Leuten verstanden werden, und meine Fragen an diese wenigen Leute zu ihnen führen oft zu widersprüchlichen Erklärungen):
MemTotal: 395144 kB
MemFree: 184936 kB
Buffers: 880 kB
Cached: 84104 kB
SwapCached: 0 kB
MemTotal
ist die Gesamtmenge an Speicher, die dem Kernel und dem Benutzerbereich zur Verfügung steht (oft weniger als der tatsächliche physische Arbeitsspeicher des Geräts, da ein Teil dieses Arbeitsspeichers für das Funkgerät, DMA-Puffer usw. benötigt wird).
MemFree
ist die Menge an RAM, die überhaupt nicht genutzt wird. Die Zahl, die Sie hier sehen, ist sehr hoch; typischerweise sind es auf einem Android-System nur ein paar MB, da wir versuchen, den verfügbaren Speicher zu nutzen, um Prozesse am Laufen zu halten
Cached
ist der Arbeitsspeicher, der für Dateisystem-Caches und andere Dinge verwendet wird. Typische Systeme benötigen dafür etwa 20 MB, um zu vermeiden, dass sie in schlechte Paging-Zustände geraten. Der Android "Out of Memory Killer" ist auf ein bestimmtes System abgestimmt, um sicherzustellen, dass Hintergrundprozesse beendet werden, bevor der zwischengespeicherte Arbeitsspeicher zu sehr von ihnen beansprucht wird und zu einem solchen Paging führt.
Ja, Sie können Speicherinformationen programmatisch abrufen und entscheiden, ob Sie speicherintensive Arbeiten durchführen wollen.
Abrufen der VM Heap Size durch Aufruf:
Runtime.getRuntime().totalMemory();
Abrufen von zugewiesenem VM-Speicher durch Aufruf:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
VM Heap Size Limit durch Aufruf abrufen:
Runtime.getRuntime().maxMemory()
Abrufen von nativem zugewiesenem Speicher durch Aufruf:
Debug.getNativeHeapAllocatedSize();
Ich habe eine Anwendung erstellt, um das OutOfMemoryError-Verhalten herauszufinden und die Speichernutzung zu überwachen.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
Sie erhalten den Quellcode unter https://github.com/coocood/oom-research
Dies ist eine unfertige Arbeit, aber das ist es, was ich nicht verstehe:
ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );
List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
}
Collection<Integer> keys = pidMap.keySet();
for(int key : keys)
{
int pids[] = new int[1];
pids[0] = key;
android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(pids);
for(android.os.Debug.MemoryInfo pidMemoryInfo: memoryInfoArray)
{
Log.i(TAG, String.format("** MEMINFO in pid %d [%s] **\n",pids[0],pidMap.get(pids[0])));
Log.i(TAG, " pidMemoryInfo.getTotalPrivateDirty(): " + pidMemoryInfo.getTotalPrivateDirty() + "\n");
Log.i(TAG, " pidMemoryInfo.getTotalPss(): " + pidMemoryInfo.getTotalPss() + "\n");
Log.i(TAG, " pidMemoryInfo.getTotalSharedDirty(): " + pidMemoryInfo.getTotalSharedDirty() + "\n");
}
}
Warum wird die PID nicht dem Ergebnis in activityManager.getProcessMemoryInfo() zugeordnet? Es ist klar, dass Sie die resultierenden Daten aussagekräftig machen wollen. Warum also hat Google es so schwierig gemacht, die Ergebnisse zu korrelieren? Das derzeitige System funktioniert nicht einmal gut, wenn ich die gesamte Speichernutzung verarbeiten möchte, da das zurückgegebene Ergebnis ein Array von Android.os.Debug.MemoryInfo-Objekten ist, aber keines dieser Objekte sagt einem, mit welchen Pids sie verbunden sind. Wenn Sie einfach ein Array mit allen Pids übergeben, haben Sie keine Möglichkeit, die Ergebnisse zu verstehen. So wie ich die Verwendung verstehe, macht es keinen Sinn, mehr als eine Pid auf einmal zu übergeben, und wenn das der Fall ist, warum sollte dann activityManager.getProcessMemoryInfo() nur ein int-Array annehmen?
Die von Hackbod ist eine der besten Antworten auf Stack Overflow. Sie wirft Licht auf ein sehr obskures Thema. Sie hat mir sehr geholfen.
Eine weitere sehr hilfreiche Ressource ist dieses Video, das man gesehen haben muss: Google I/O 2011: Speicherverwaltung für Android-Apps
UPDATE
Process Stats, ein Dienst, mit dem Sie herausfinden können, wie Ihre Anwendung den Arbeitsspeicher verwaltet, wird in diesem Blogbeitrag erläutert Prozess-Statistiken: Verstehen, wie Ihre App den RAM nutzt von Dianne Hackborn:
Android Studio 0.8.10+ hat ein unglaublich nützliches Werkzeug namens Speicher-Monitor .
Wozu es gut ist:
- Anzeige von verfügbarem und genutztem Speicher in einem Diagramm und von Garbage Collection Ereignisse im Zeitverlauf.
- Schnelles Testen, ob die Langsamkeit der Anwendung möglicherweise auf auf übermäßige Garbage Collection-Ereignisse zurückzuführen ist.
- Schnelles Testen ob App-Abstürze mit Speichermangel zusammenhängen können.
Abbildung 1. Erzwingen eines GC-Ereignisses (Garbage Collection) im Android Memory Monitor
Sie können damit sehr gute Informationen über den RAM-Echtzeitverbrauch Ihrer Anwendung erhalten.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.