Frage Wie kann ich die Speichernutzung meiner Anwendung in Android ermitteln?


Wie kann ich den in meiner Android-Anwendung verwendeten Speicher programmatisch finden?

Ich hoffe, es gibt einen Weg, es zu tun. Und wie bekomme ich auch den freien Speicher des Telefons?


745
2018-02-19 17:12


Ursprung


Antworten:


Beachten Sie, dass die Speichernutzung auf modernen Betriebssystemen wie Linux ein ist äußerst komplizierter und schwer verständlicher Bereich. Tatsächlich sind die Chancen, dass Sie tatsächlich richtig interpretieren, welche Zahlen Sie erhalten, extrem niedrig. (Jedes Mal, wenn ich die Speicherbelegungszahlen mit anderen Ingenieuren betrachte, gibt es immer eine lange Diskussion darüber, was sie eigentlich meinen, was nur zu einer vagen Schlussfolgerung führt.)

Hinweis: Wir haben jetzt viel umfangreichere Dokumentation zu Verwalten des App-Speichers Das deckt einen Großteil des Materials ab und ist aktueller mit dem Status von Android.

Die erste Sache ist, wahrscheinlich den letzten Teil dieses Artikels zu lesen, der eine Diskussion darüber hat, wie der Speicher auf Android verwaltet wird:

Service-API-Änderungen beginnen mit Android 2.0

Jetzt ActivityManager.getMemoryInfo() ist unsere höchste API für die Gesamtspeicherauslastung. Dies dient hauptsächlich dazu, einer Anwendung zu zeigen, wie nahe das System dazu kommt, keinen Speicher mehr für Hintergrundprozesse zu haben und somit benötigte Prozesse wie Dienste zu löschen. Für reine Java-Anwendungen sollte dies wenig nutzen, da das Java-Heap-Limit teilweise vorhanden ist, um zu verhindern, dass eine App das System bis zu diesem Punkt belastet.

Auf niedrigerer Ebene können Sie die Debug-API verwenden, um Informationen zur Speichernutzung auf Kernel-Ebene zu erhalten: android.os.Debug.MemoryInfo

Hinweis ab 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, und SharedDirty... nun fängt der Spaß an.

Viel Speicher in Android (und Linux-Systemen im Allgemeinen) wird tatsächlich über mehrere Prozesse verteilt. Wie viel Speicher ein Prozess nutzt, ist also nicht klar. Fügen Sie darüber hinaus Auslagerung auf die Festplatte (geschweige denn tauschen, die wir nicht auf Android verwenden) und es ist noch weniger klar.

Wenn Sie also den gesamten tatsächlich in jedem Prozess zugewiesenen RAM verwenden und alle Prozesse addieren würden, würden Sie wahrscheinlich eine Zahl erreichen, die viel größer ist als der tatsächliche Gesamt-RAM.

Das Pss number ist eine Metrik, die der Kernel berechnet, die die gemeinsame Nutzung von Speichern berücksichtigt. Grundsätzlich wird jede Seite des 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 addieren, um den gesamten RAM zu sehen, den sie verwenden, und pss zwischen den Prozessen vergleichen, um eine grobe Vorstellung von ihrem relativen Gewicht zu erhalten.

Die andere interessante Metrik ist hier PrivateDirtyDies ist im Grunde die Menge an RAM innerhalb des Prozesses, die nicht auf die Festplatte ausgelagert werden kann (sie wird nicht von den gleichen Daten auf der Festplatte gesichert) und wird nicht mit anderen Prozessen geteilt. Eine andere Möglichkeit, dies zu betrachten, ist der RAM, der dem System zur Verfügung gestellt wird, wenn dieser Prozess aufhört (und wahrscheinlich schnell in Caches und andere Verwendungen davon subsummiert wird).

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

Verwenden adbEs gibt viele Informationen, die Sie über die Speicherbenutzung eines laufenden Systems erhalten können. Eine übliche ist der Befehl adb shell dumpsys meminfo Dies wird eine Menge Informationen über die Speicherbenutzung jedes Java-Prozesses ausspucken, die die obigen Informationen sowie eine Vielzahl anderer Dinge enthalten. Sie können z. B. auch den Namen oder die PID eines einzelnen Prozesses markieren adb shell dumpsys meminfo system gib mir den Systemprozess:

** MEMINFO in PID 890 [System] **
                    native dalvik andere insgesamt
            Größe: 10940 7047 N / A 17987
       zugewiesen: 8943 5516 N / A 14459
            frei: 336 1531 N / A 1867
           (Pss): 4585 9282 11916 25783
  (geteilt schmutzig): 2184 3596 916 6696
    (Priv schmutzig): 4504 5956 7456 17916

 Objekte
           Aufrufe: 149 ViewRoots: 4
     AppContexte: 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 Teil ist der Hauptteil, wo size ist die Gesamtgröße im Adressraum eines bestimmten Heap, allocated ist der kb der tatsächlichen Zuweisungen, die der Haufen denkt, free ist das verbleibende kb frei, das der Heap für zusätzliche Zuordnungen hat, und pss und priv dirty sind die gleichen wie zuvor für Seiten, die mit den einzelnen Heaps verknüpft sind.

Wenn Sie nur die Speichernutzung für alle Prozesse anzeigen möchten, können Sie den Befehl verwenden adb shell procrank. Die Ausgabe davon auf dem gleichen 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 / Behälter / Einbau
   60 396K 392K 93K 84K / System / Fach / Keystore
   51 280K 276K 74K 68K / System / Behälter / Servicemanager
   54 256K 252K 69K 64K / system / bin / debuggerd

Hier das Vss und Rss Spalten sind im Grunde Rauschen (das sind der direkte Adressraum und die RAM-Nutzung eines Prozesses, wobei, wenn Sie die RAM-Nutzung über Prozesse hinweg addieren, eine lächerlich große Zahl entsteht).

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

Interessante Sache hier zu beachten: Pss und Uss sind etwas (oder mehr als ein bisschen) anders als das, was wir sahen meminfo. Warum das? Nun procrank verwendet einen anderen Kernel-Mechanismus, um seine Daten zu sammeln meminfo tut, und sie geben etwas andere Ergebnisse. Warum das? Ehrlich gesagt habe ich keine Ahnung. Ich glaube procrank mag der genauere sein ... aber wirklich, das hinterlässt einfach den Punkt: "Nimm irgendwelche Speicherinformationen mit, die du mit einem Körnchen Salz bekommst; oft ein sehr großes Korn."

Endlich gibt es den Befehl adb shell cat /proc/meminfo Dies gibt eine Zusammenfassung der gesamten Speicherauslastung des Systems. Hier gibt es viele Daten, nur die ersten paar, die es wert sind, besprochen zu werden (und die restlichen, die von wenigen Menschen verstanden werden, und meine Fragen über diese wenigen Leute, die oft zu widersprüchlichen Erklärungen führen):

MemTotal: 395144 kB
MemFree: 184936 kB
Puffer: 880 kB
Im Cache: 84104 kB
SwapCached: 0 kB

MemTotal ist die Gesamtmenge an Speicher, die für den Kernel und den Benutzerraum verfügbar ist (oft weniger als der tatsächliche physikalische RAM des Geräts, da ein Teil dieses RAMs für das Radio, DMA-Puffer usw. benötigt wird).

MemFree ist die Menge an RAM, die überhaupt nicht verwendet wird. Die Nummer, die Sie hier sehen, ist sehr hoch; In der Regel wären dies bei einem Android-System nur ein paar MB, da wir versuchen, den verfügbaren Speicher zu nutzen, um die Prozesse in Gang zu halten

Cached ist der RAM, der für Dateisystem-Caches und andere solche Dinge verwendet wird. Typische Systeme benötigen hierfür 20 MB, um nicht in schlechte Paging-Zustände zu geraten. Der Android-Speicherkiller ist auf ein bestimmtes System abgestimmt, um sicherzustellen, dass Hintergrundprozesse beendet werden, bevor der zwischengespeicherte RAM von ihnen zu sehr verbraucht wird, um zu einem solchen Paging zu führen.


969
2018-02-19 21:44



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

Abrufen der VM-Heap-Größe durch Aufrufen von:

Runtime.getRuntime().totalMemory();

Rufen Sie den zugewiesenen VM-Speicher ab, indem Sie Folgendes aufrufen:

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

Erhalte VM-Heap-Größenbeschränkung durch Aufruf von:

Runtime.getRuntime().maxMemory()

Nativ zugewiesenen Speicher abrufen, indem Sie Folgendes aufrufen:

Debug.getNativeHeapAllocatedSize();

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

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

Sie können den Quellcode unter erhalten https://github.com/cocood/oom-research


69
2017-12-01 05:49



Dies ist eine Arbeit im Gange, 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 dem Ergebnis in activityManager.getProcessMemoryInfo () zugeordnet? Natürlich möchten Sie die resultierenden Daten aussagekräftig machen. Warum hat Google es so schwierig gemacht, die Ergebnisse zu korrelieren? Das aktuelle System funktioniert nicht einmal gut, wenn ich die gesamte Speicherauslastung 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 verknüpft sind. Wenn Sie einfach ein Array aller PIDs übergeben, haben Sie keine Möglichkeit, die Ergebnisse zu verstehen. Wie ich es verstehe, macht es es sinnlos, mehr als eine PID zu einem Zeitpunkt zu übergeben, und wenn das der Fall ist, warum sollte activityManager.getProcessMemoryInfo () nur ein int-Array verwenden?


48
2017-08-13 18:32



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

Eine weitere sehr hilfreiche Ressource ist dieses Video, das Sie unbedingt sehen müssen: Google I / O 2011: Speicherverwaltung für Android-Apps


AKTUALISIEREN:

Process Stats, ein Dienst, um zu erfahren, wie Ihre App den Speicher verwaltet, wird im Blogpost erläutert Prozessstatistik: Verstehen, wie Ihre App RAM verwendet von Dianne Hackborn:


24
2017-12-21 16:49



Android Studio 0.8.10+ hat ein unglaublich nützliches Tool namens Speicherüberwachung.

enter image description here

Wofür ist es gut?

  • Anzeigen von verfügbarem und verwendetem Speicher in einem Diagramm und Garbage Collection   Ereignisse im Laufe der Zeit.
  • Schnell testen, ob App-Langsamkeit sein könnte   im Zusammenhang mit übermäßigen Garbage Collection-Ereignissen.
  • Schnell testen   Ob App-Abstürze auftreten können, wenn der Arbeitsspeicher knapp wird.

enter image description here

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

Sie können viele nützliche Informationen über die Echtzeit-Speicherauslastung Ihrer App abrufen, indem Sie sie verwenden.


19
2017-08-12 12:40



1) Ich denke 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
2018-02-19 17:31



Wir haben herausgefunden, dass alle Standardmethoden, um den gesamten Speicher des aktuellen Prozesses zu erhalten, einige Probleme haben.

  • Runtime.getRuntime().totalMemory(): Gibt nur JVM-Speicher zurück
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() und alles andere basiert auf /proc/meminfo - gibt Speicherinformationen über alle kombinierten Prozesse zurück (z. android_util_Process.cpp)
  • Debug.getNativeHeapAllocatedSize() - Verwendet mallinfo() die Informationen über Speicherzuordnungen zurückgeben, die von ausgeführt werden malloc() und verwandte Funktionen nur (siehe android_os_Debug.cpp)
  • Debug.getMemoryInfo() - macht den Job, aber es ist zu langsam. Es dauert ungefähr 200ms auf Nexus 6 für einen einzelnen Anruf. Der Leistungs-Overhead macht diese Funktion für uns nutzlos, da wir sie regelmäßig anrufen und jeder Anruf ziemlich auffällig ist (vgl android_os_Debug.cpp)
  • ActivityManager.getProcessMemoryInfo(int[]) - Anrufe Debug.getMemoryInfo() intern (siehe ActivityManagerService.java)

Schließlich haben wir den 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 kehrt zurück VmRSS metrisch. Mehr Informationen dazu finden Sie hier: ein, zwei und drei.


P.S. Mir ist aufgefallen, dass dem Thema immer noch ein tatsächlicher und einfacher Code-Ausschnitt fehlt schätzen die private Speichernutzung des Prozesses, 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;

2
2017-08-14 02:02



Es gibt eine Menge Antwort darüber, die Ihnen bestimmt helfen wird (nach 2 Tagen leisten und Forschung über ADB-Speicher-Tools) Ich denke, dass ich mit meinem helfen kann Meinung auch.

Wie Hackbod sagt: Wenn Sie also den gesamten tatsächlich in jedem Prozess zugewiesenen RAM verwenden und alle Prozesse addieren würden, würden Sie wahrscheinlich eine Zahl erreichen, die viel größer ist als der tatsächliche Gesamt-RAM.   es gibt also keine Möglichkeit, genau die Menge an Speicher pro Prozess zu erhalten.

Aber Sie können durch eine Logik nah dran kommen ... und ich werde erzählen, wie ..

Es gibt einige API wie android.os.Debug.MemoryInfo und ActivityManager.getMemoryInfo() oben erwähnt, über die Sie vielleicht bereits gelesen und benutzt haben, aber ich werde über einen anderen Weg sprechen

Also zuerst müssen Sie ein root-Benutzer sein, damit es funktioniert. Mit der Root-Berechtigung in die Konsole gelangen su im Prozess und bekommen Sie es output and input stream. Dann pass auf id\n  (eingeben) in outputstream und schreibe es zur Prozessausgabe, wenn ein Eingangsstrom erhalten wird uid=0, Sie sind Root-Benutzer.

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

Wenn Sie am Prozess ankommen Übergeben Sie Ihren Befehl (procrank, dumpsys meminfo etc ...) mit \n statt id und nimm es inputstream und lesen, speichern Sie den Stream in Bytes [], char [] etc .. verwenden roh 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ühre deinen Befehl mit aus su

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 statt in einer Instanz von einer beliebigen API, was komplex zu speichern ist, da Sie sie manuell trennen müssen.

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


0
2018-02-20 12:37