it-swarm.com.de

Wie erfahre ich die Speicherbelegung meiner Anwendung in Android?

Wie kann ich den Speicherplatz meiner Android-Anwendung programmgesteuert finden?

Ich hoffe, dass es einen Weg gibt, dies zu tun. Wie bekomme ich außerdem den freien Speicher des Telefons?

767
Andrea Baccega

Beachten Sie, dass die Speichernutzung auf modernen Betriebssystemen wie Linux ein extrem komplizierter und schwer verständlicher Bereich ist. Tatsächlich ist die Wahrscheinlichkeit, dass Sie tatsächlich die Zahlen richtig interpretieren, die Sie erhalten, äußerst gering. (So ​​gut wie jedes Mal, wenn ich mir mit anderen Ingenieuren die Speicherbelegungszahlen ansehe, gibt es eine lange Diskussion darüber, was sie eigentlich bedeuten, was nur zu einer vagen Schlussfolgerung führt.)

Hinweis: Wir haben jetzt eine viel umfangreichere Dokumentation zu Speicherverwaltung Ihrer App , die einen Großteil des Materials hier abdeckt und aktueller mit dem Stand von Android ist .

Als Erstes lesen Sie wahrscheinlich den letzten Teil dieses Artikels, in dem erläutert wird, wie der Speicher unter Android verwaltet wird:

Änderungen der Service-API ab Android 2.

Jetzt ist ActivityManager.getMemoryInfo() unsere API auf höchster Ebene, um die gesamte Speichernutzung zu untersuchen. Dies ist hauptsächlich dazu da, um einer Anwendung zu zeigen, wie nahe das System rückt, um keinen Speicher mehr für Hintergrundprozesse zu haben, und damit zu beginnen, benötigte Prozesse wie Dienste zu beenden. Für reine Java - Anwendungen sollte dies wenig hilfreich sein, da das Java - Heap-Limit teilweise besteht, um zu vermeiden, dass eine App das System bis zu diesem Punkt belasten kann.

In einer niedrigeren Version können Sie die Debug-API verwenden, um grundlegende Informationen zur Speichernutzung auf Kernel-Ebene abzurufen: Android.os.Debug.MemoryInfo

Beachten Sie, dass es ab 2.0 auch eine API gibt, ActivityManager.getProcessMemoryInfo, um diese Informationen zu einem anderen Prozess abzurufen: ActivityManager.getProcessMemoryInfo (int []

Dies gibt eine einfache 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 und SharedDirty ... nun beginnt der Spaß.

Viel Speicher in Android (und Linux-Systemen im Allgemeinen) wird tatsächlich von mehreren Prozessen gemeinsam genutzt. Es ist also nicht klar, wie viel Speicher ein Prozess belegt. Fügen Sie zusätzlich das Auslagern auf die Festplatte hinzu (geschweige denn das Auslagern, das wir auf Android nicht verwenden), und es ist noch weniger klar.

Wenn Sie also den gesamten physischen RAM, der tatsächlich jedem Prozess zugeordnet ist, verwenden und alle Prozesse addieren, erhalten Sie wahrscheinlich eine Zahl, die viel größer ist als der tatsächliche Gesamtspeicher.

Die Zahl Pss ist eine vom Kernel berechnete Metrik, die die gemeinsame Nutzung des Speichers berücksichtigt. Grundsätzlich wird jede Seite von RAM in einem Prozess durch ein Verhältnis der Anzahl anderer Prozesse skaliert, die diese Seite ebenfalls verwenden. Auf diese Weise können Sie (theoretisch) die PSS über alle Prozesse hinweg addieren, um die Gesamtmenge RAM zu sehen, die sie verwenden, und PSS zwischen Prozessen vergleichen, um eine ungefähre Vorstellung von ihrem relativen Gewicht zu erhalten.

Die andere interessante Metrik ist PrivateDirty, die im Grunde genommen die Menge an RAM innerhalb des Prozesses ist, die nicht auf die Festplatte ausgelagert werden kann (sie wird nicht von denselben Daten auf der Festplatte gesichert) und nicht gemeinsam genutzt mit anderen Prozessen. Eine andere Möglichkeit, dies zu betrachten, ist das RAM, das dem System zur Verfügung steht, wenn dieser Prozess beendet ist (und wahrscheinlich schnell in Caches und andere Verwendungen davon zusammengefasst wird).

Das sind so ziemlich die SDK-APIs dafür. Mit Ihrem Gerät können Sie als Entwickler jedoch noch mehr tun.

Mit adb erhalten Sie viele Informationen über die Speichernutzung eines laufenden Systems. Ein gebräuchlicher Befehl ist adb Shell dumpsys meminfo, der eine Reihe von Informationen über die Speichernutzung jedes Java - Prozesses ausgibt und die obigen Informationen sowie eine Vielzahl anderer Dinge enthält. Sie können auch den Namen oder die PID eines einzelnen Prozesses eingeben, um zu sehen, wie zum Beispiel adb Shell dumpsys meminfo system den Systemprozess angibt:

 ** MEMINFO in pid 890 [system] ** 
 Native dalvik other gesamt 
 Größe: 10940 7047 nv 17987 
 Zugeteilt: 8943 5516 nv 14459 
 Frei: 336 1531 N/A 1867 
 (Pss): 4585 9282 11916 25783 
 (Gemeinsam verschmutzt): 2184 3596 916 6696 
 (Privat verschmutzt) : 4504 5956 7456 17916 
 
 Objekte 
 Aufrufe: 149 ViewRoots: 4 
 App-Kontexte: 13 Aktivitäten: 0 
 Assets: 4 AssetManager: 4 
 Lokale Ordner: 141 Proxy-Ordner: 158 
 Todesempfänger: 49 
 OpenSSL-Sockets: 0 
 
 SQL 
 Heap: 205 dbFiles : 0 
 NumPagers: 0 inactivePageKB: 0 
 ActivePageKB: 0 

Der obere Abschnitt ist der Hauptabschnitt, in dem size die Gesamtgröße im Adressraum eines bestimmten Heaps ist, allocated die KB der tatsächlichen Zuweisungen, die Heap für möglich hält, free ist Verbleibende KB, die der Heap für zusätzliche Zuweisungen frei hat, und pss und priv dirty sind die gleichen, die zuvor für die mit jedem der Heaps verbundenen Seiten beschrieben wurden.

Wenn Sie nur die Speichernutzung für alle Prozesse anzeigen möchten, können Sie den Befehl adb Shell procrank verwenden. Die Ausgabe auf demselben System sieht folgendermaßen 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 9 .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. lateinamerikanisch 
 888 25728 K 25724 K 5774 K 3668 K zygote 
 977 24100 K 24096 K 5667 K 4340 K Android.process.acore 
 ... 
 59 336 K 332 K 99 K 92 K/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 sind die Spalten Vss und Rss im Grunde genommen Rauschen (dies sind der direkte Adressraum und die RAM - Nutzung eines Prozesses, wenn Sie die RAM - Nutzung über addieren Prozesse erhalten Sie eine lächerlich große Anzahl).

Pss ist wie wir zuvor gesehen haben, und Uss ist Priv Dirty.

Interessante Anmerkung: Pss und Uss unterscheiden sich geringfügig (oder mehr als geringfügig) von dem, was wir in meminfo gesehen haben. Warum das? Nun, procrank verwendet einen anderen Kernel-Mechanismus, um seine Daten zu sammeln als meminfo, und sie liefern leicht unterschiedliche Ergebnisse. Warum das? Ehrlich gesagt habe ich keine Ahnung. Ich glaube, procrank mag der genauere sein ... aber wirklich, hier bleibt nur der Punkt: "Nehmen Sie alle Speicherinformationen, die Sie mit einem Salzkorn erhalten, oft ein sehr großes Korn."

Schließlich gibt es den Befehl adb Shell cat /proc/meminfo, der eine Zusammenfassung der gesamten Speichernutzung des Systems gibt. Hier gibt es viele Daten, nur die ersten Zahlen, die es zu diskutieren gilt (und die übrigen, die nur von wenigen verstanden werden, und meine Fragen an diese wenigen Personen führen häufig zu widersprüchlichen Erklärungen):

 MemTotal: 395144 kB 
 MemFree: 184936 kB 
 Puffer: 880 kB 
 Cached: 84104 kB 
 SwapCached: 0 kB 

MemTotal ist die Gesamtmenge an Speicher, die dem Kernel und dem Benutzerspeicherplatz zur Verfügung steht (häufig weniger als der tatsächliche physische RAM des Geräts, da ein Teil davon RAM für das Radio benötigt wird, DMA Puffer usw.).

MemFree ist die Menge an RAM, die überhaupt nicht verwendet wird. Die Zahl, die Sie hier sehen, ist sehr hoch. In der Regel sind dies auf einem Android System nur wenige MB, da wir versuchen, den verfügbaren Speicher zu nutzen, um die Prozesse am Laufen zu halten

Cached ist das RAM, das für Dateisystem-Caches und andere solche Dinge verwendet wird. Typische Systeme müssen etwa 20 MB groß sein, um zu vermeiden, dass sie in einen schlechten Paging-Status geraten. Der Killer Android für nicht genügend Arbeitsspeicher ist auf ein bestimmtes System abgestimmt, um sicherzustellen, dass Hintergrundprozesse abgebrochen werden, bevor der zwischengespeicherte RAM von ihnen zu stark beansprucht wird, um zu einem solchen Paging zu führen.

998
hackbod

Ja, Sie können Speicherinformationen programmgesteuert abrufen und entscheiden, ob speicherintensive Arbeit ausgeführt werden soll.

Erhalten Sie VM Heap-Größe, indem Sie Folgendes anrufen:

Runtime.getRuntime().totalMemory();

Erhalten Sie zugewiesenen VM Speicher durch Aufruf von:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Erhalten Sie VM Heap-Größenlimit, indem Sie Folgendes anrufen:

Runtime.getRuntime().maxMemory()

Holen Sie sich systemeigen zugewiesenen Speicher durch Aufruf von:

Debug.getNativeHeapAllocatedSize();

Ich habe eine App erstellt, um das Verhalten von OutOfMemoryError herauszufinden und die Speicherauslastung zu überwachen.

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

Den Quellcode erhalten Sie unter https://github.com/coocood/oom-research

74
coocood

Dies ist eine Arbeit in Arbeit, aber das verstehe ich nicht:

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 in activityManager.getProcessMemoryInfo () dem Ergebnis zugeordnet? Natürlich möchten Sie die resultierenden Daten aussagekräftig machen. Warum hat es Google so schwierig gemacht, die Ergebnisse zu korrelieren? Das aktuelle System funktioniert nicht einmal gut, wenn ich die gesamte Speicherbelegung verarbeiten möchte, da das zurückgegebene Ergebnis ein Array von Android.os.Debug.MemoryInfo-Objekten ist, aber keines dieser Objekte sagt Ihnen tatsächlich, mit welchen Pids sie verbunden sind. Wenn Sie einfach ein Array aller Pids übergeben, haben Sie keine Möglichkeit, die Ergebnisse zu verstehen. Wenn ich die Verwendung verstehe, ist es sinnlos, mehr als eine PID gleichzeitig zu übergeben. Wenn dies der Fall ist, warum sollte es so sein, dass activityManager.getProcessMemoryInfo () nur ein int-Array verwendet?

50
Ryan Beesley

Hackbod's ist eine der besten Antworten auf Stack Overflow. Es wirft Licht auf ein sehr dunkles Motiv. Es hat mir sehr geholfen.

Eine weitere wirklich hilfreiche Ressource ist dieses Video, das Sie gesehen haben müssen: Google I/O 2011: Speicherverwaltung für Android Apps


UPDATE:

Process Stats, ein Dienst, um herauszufinden, wie Ihre App Speicher verwaltet, der im Blogbeitrag Prozessstatistik: Verstehen, wie Ihre App RAMverwendet, _ von Dianne Hackborn: 

24
Xavi Gil

Android Studio 0.8.10+ hat ein unglaublich nützliches Tool namens Memory Monitor eingeführt.

 enter image description here

Was ist gut für:

  • Anzeigen des verfügbaren und verwendeten Speichers in einem Diagramm und der Garbage Collection Ereignisse im Laufe der Zeit. 
  • Schnelles Testen, ob die Langsamkeit der App möglicherweise __ ist. im Zusammenhang mit übermäßigen Müllsammelereignissen. 
  • Schnell testen Ob die App abstürzt, hängt möglicherweise davon ab, dass nicht genügend Speicherplatz zur Verfügung steht.

 enter image description here

Abbildung 1. Erzwingen eines GC-Ereignisses (Garbage Collection) auf dem Android Memory Monitor

Sie können viele Informationen über den Echtzeitverbrauch Ihrer App RAM erhalten, wenn Sie sie verwenden. 

19
Machado

1) Ich vermute nicht, zumindest nicht von Java.
2)

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
16
yanchenko

Wir haben herausgefunden, dass alle gängigen Möglichkeiten, den Gesamtspeicher des aktuellen Prozesses zu erhalten, einige Probleme haben.

  • Runtime.getRuntime().totalMemory(): Gibt nur den JVM-Speicher zurück
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() und alles andere basierend auf /proc/meminfo - gibt Speicherinformationen über alle kombinierten Prozesse zurück (z. B. Android_util_Process.cpp )
  • Debug.getNativeHeapAllocatedSize() - verwendet mallinfo(), die nur Informationen über die von malloc() durchgeführten Speicherzuordnungen und zugehörige Funktionen zurückgibt (siehe Android_os_Debug.cpp )
  • Debug.getMemoryInfo() - erledigt den Job, aber es ist zu langsam. Es dauert ungefähr 200ms für Nexus 6 für einen einzelnen Anruf. Der Performance-Overhead macht diese Funktion für uns unbrauchbar, da wir sie regelmäßig nennen, und jeder Aufruf ist ziemlich auffällig (siehe Android_os_Debug.cpp ).
  • ActivityManager.getProcessMemoryInfo(int[]) - ruft intern Debug.getMemoryInfo() auf (siehe ActivityManagerService.Java )

Schließlich haben wir folgenden Code verwendet:

const long pageSize = 4 * 1024; //`sysconf(_SC_PAGESIZE)`
string stats = File.ReadAllText("/proc/self/statm");
var statsArr = stats.Split(new [] {' ', '\t', '\n'}, 3);

if( statsArr.Length < 2 )
    throw new Exception("Parsing error of /proc/self/statm: " + stats);

return long.Parse(statsArr[1]) * pageSize;

Es gibt VmRSS Metrik zurück. Weitere Informationen dazu finden Sie hier: Eins , Zwei und Drei .


P.S. Mir ist aufgefallen, dass dem Thema immer noch ein tatsächlicher und einfacher Code-Ausschnitt fehlt, wie schätzen die private Speicherauslastung des Prozesses _ ist, wenn die Leistung keine kritische Anforderung ist:

Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
long res = memInfo.getTotalPrivateDirty();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat) 
    res += memInfo.getTotalPrivateClean(); 

return res * 1024L;
3

In Android Studio 3.0 wurde der Android-Profiler eingeführt, um zu verstehen, wie Ihre App Ressourcen für CPU, Arbeitsspeicher, Netzwerk und Batterie verwendet.

https://developer.Android.com/studio/profile/Android-profiler

 enter image description here

0
Akash Patel

Es gibt oben eine Menge Antworten, die Ihnen definitiv helfen werden, aber (nach 2 Tagen Erstattung und Recherche zu AdB-Speicher-Tools) Ich denke, ich kann mit meiner Meinung helfen auch.

Wie sagt Hackbod: Wenn Sie also alle physischen RAM tatsächlich jedem Prozess zugeordnet haben und alle Prozesse addieren, Sie würden wahrscheinlich eine Zahl haben, die viel größer ist als der tatsächliche Gesamtspeicher. Sie können also auf keinen Fall die genaue Größe des Speichers pro Prozess ermitteln.

Aber Sie können sich dem durch eine Logik nähern .. und ich werde sagen, wie ..

Es gibt einige APIs wie Android.os.Debug.MemoryInfo Und ActivityManager.getMemoryInfo(), über die Sie möglicherweise bereits gelesen und verwendet wurden, aber ich werde auf andere Weise darüber sprechen

Zunächst müssen Sie ein Root-Benutzer sein, damit es funktioniert. Rufen Sie die Konsole mit Root-Rechten auf, indem Sie su in process ausführen und dessen output and input stream Abrufen. Übergeben Sie dann id\n (enter) in ouputstream und schreiben Sie es, um die Ausgabe zu verarbeiten. If erhält einen Eingabestream mit uid=0, you sind root-Benutzer.

Nun ist hier die Logik, die Sie im obigen Prozess verwenden werden

Wenn Sie ouputstream von process übergeben Sie Ihren Befehl (procrank, dumpsys meminfo etc ...) mit \n Anstelle von id erhalten und dessen inputstream abrufen und lesen, speichern Sie das Stream in Bytes [], Zeichen [] usw. Verwenden Sie raw Daten..und Sie sind fertig !!!!!

genehmigung :

<uses-permission Android:name="Android.permission.FACTORY_TEST"/>

Überprüfen Sie, ob Sie Root-Benutzer sind:

// su command to get root access
Process process = Runtime.getRuntime().exec("su");         
DataOutputStream dataOutputStream = 
                           new DataOutputStream(process.getOutputStream());
DataInputStream dataInputStream = 
                           new DataInputStream(process.getInputStream());
if (dataInputStream != null && dataOutputStream != null) {
   // write id to console with enter
   dataOutputStream.writeBytes("id\n");                   
   dataOutputStream.flush();
   String Uid = dataInputStream.readLine();
   // read output and check if uid is there
   if (Uid.contains("uid=0")) {                           
      // you are root user
   } 
}

Führen Sie Ihren Befehl mit su aus

Process process = Runtime.getRuntime().exec("su");         
DataOutputStream dataOutputStream = 
                           new DataOutputStream(process.getOutputStream());
if (dataOutputStream != null) {
 // adb command
 dataOutputStream.writeBytes("procrank\n");             
 dataOutputStream.flush();
 BufferedInputStream bufferedInputStream = 
                     new BufferedInputStream(process.getInputStream());
 // this is important as it takes times to return to next line so wait
 // else you with get empty bytes in buffered stream 
 try {
       Thread.sleep(10000);
 } catch (InterruptedException e) {                     
       e.printStackTrace();
 }
 // read buffered stream into byte,char etc.
 byte[] bff = new byte[bufferedInputStream.available()];
 bufferedInputStream.read(bff);
 bufferedInputStream.close();
 }
}

logcat: result

Sie erhalten Rohdaten in einer einzelnen Zeichenfolge von der Konsole anstatt in einigen Fällen von einer API, deren Speicherung komplex ist, da Sie sie manuell trennen müssen.

Dies ist nur ein Versuch, bitte schlagen Sie mir vor, wenn ich etwas verpasst habe

0
Iamat8