834 Stimmen

Wie kann ich die Speichernutzung meiner Anwendung in Android ermitteln?

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?

1040voto

hackbod Punkte 89543

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.

84voto

coocood Punkte 1356

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

52voto

Ryan Beesley Punkte 637

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?

24voto

Xavi Gil Punkte 11102

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:

18voto

Machado Punkte 13465

Android Studio 0.8.10+ hat ein unglaublich nützliches Werkzeug namens Speicher-Monitor .

enter image description here

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.

enter image description here

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.com

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.

Powered by:

X