Come posso trovare la memoria utilizzata sulla mia applicazione Android, a livello di codice?
Spero che ci sia un modo per farlo. Inoltre, come posso ottenere anche la memoria libera del telefono?
Come posso trovare la memoria utilizzata sulla mia applicazione Android, a livello di codice?
Spero che ci sia un modo per farlo. Inoltre, come posso ottenere anche la memoria libera del telefono?
Risposte:
Si noti che l'utilizzo della memoria su moderni sistemi operativi come Linux è un'area estremamente complicata e di difficile comprensione. In effetti, le possibilità che tu interpreti correttamente qualsiasi numero tu ottenga è estremamente bassa. (Praticamente ogni volta che guardo i numeri di utilizzo della memoria con altri ingegneri, c'è sempre una lunga discussione su cosa significano effettivamente che si traduce solo in una vaga conclusione.)
Nota: ora abbiamo una documentazione molto più ampia sulla gestione della memoria della tua app che copre gran parte del materiale qui ed è più aggiornato con lo stato di Android.
La prima cosa è probabilmente leggere l'ultima parte di questo articolo che discute di come viene gestita la memoria su Android:
Modifiche all'API di servizio a partire da Android 2.0
Ora ActivityManager.getMemoryInfo()
è la nostra API di massimo livello per esaminare l'utilizzo complessivo della memoria. Questo è principalmente lì per aiutare un utente a valutare quanto il sistema si avvicini a non avere più memoria per i processi in background, quindi è necessario iniziare a uccidere i processi necessari come i servizi. Per le applicazioni Java pure, questo dovrebbe essere di scarsa utilità, poiché il limite di heap Java è in parte lì per evitare che un'app sia in grado di stressare il sistema fino a questo punto.
Passando al livello inferiore, è possibile utilizzare l'API di debug per ottenere informazioni non elaborate a livello di kernel sull'utilizzo della memoria: android.os.Debug.MemoryInfo
Nota a partire da 2.0 c'è anche un'API, ActivityManager.getProcessMemoryInfo
per ottenere queste informazioni su un altro processo: ActivityManager.getProcessMemoryInfo (int [])
Ciò restituisce una struttura MemoryInfo di basso livello con tutti questi dati:
/** 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;
Ma da quale sia la differenza tra il Pss
, PrivateDirty
e SharedDirty
... beh ora inizia il divertimento.
Molta memoria in Android (e sistemi Linux in generale) è in realtà condivisa tra più processi. Quindi quanta memoria utilizza un processo non è davvero chiara. Aggiungete inoltre il paging su disco (per non parlare dello scambio che non utilizziamo su Android) ed è ancora meno chiaro.
Quindi, se dovessi prendere tutta la RAM fisica effettivamente mappata su ciascun processo e sommare tutti i processi, probabilmente finiresti con un numero molto maggiore della RAM totale effettiva.
Il Pss
numero è una metrica calcolata dal kernel che tiene conto della condivisione della memoria - in pratica ogni pagina di RAM in un processo viene ridimensionata in base a un rapporto del numero di altri processi che usano anche quella pagina. In questo modo è possibile (in teoria) sommare i pss in tutti i processi per vedere la RAM totale che stanno usando e confrontare i pss tra i processi per avere un'idea approssimativa del loro peso relativo.
L'altra metrica interessante qui è PrivateDirty
, che è fondamentalmente la quantità di RAM all'interno del processo che non può essere paginata su disco (non è supportata dagli stessi dati sul disco) e non è condivisa con altri processi. Un altro modo di vedere questo è la RAM che diventerà disponibile per il sistema quando quel processo andrà via (e probabilmente si riassume rapidamente in cache e altri usi di esso).
Sono praticamente le API dell'SDK per questo. Tuttavia, puoi fare di più come sviluppatore con il tuo dispositivo.
Utilizzando adb
, ci sono molte informazioni che puoi ottenere sull'uso della memoria di un sistema in esecuzione. Un comando comune è il comando adb shell dumpsys meminfo
che sputerà un mucchio di informazioni sull'uso della memoria di ciascun processo Java, che contiene le informazioni di cui sopra e una varietà di altre cose. Puoi anche applicare il nome o il pid di un singolo processo per vedere, ad esempio adb shell dumpsys meminfo system
darmi il processo di sistema:
** MEMINFO in pid 890 [sistema] ** nativo dalvik altro totale taglia: 10940 7047 N / A 17987 assegnato: 8943 5516 N / A 14459 gratis: 336 1531 N / A 1867 (Pss): 4585 9282 11916 25783 (condiviso sporco): 2184 3596 916 6696 (priv sporco): 4504 5956 7456 17916 Oggetti Visualizzazioni: 149 ViewRoots: 4 AppContexts: 13 Attività: 0 Attività: 4 Gestione risorse: 4 Raccoglitori locali: 141 leganti proxy: 158 Destinatari della morte: 49 Socket OpenSSL: 0 SQL heap: 205 db File: 0 numPagers: 0 inactivePageKB: 0 activePageKB: 0
La sezione superiore è quella principale, dove si size
trova la dimensione totale nello spazio degli indirizzi di un particolare heap, allocated
è il kb di allocazioni effettive che l'heap ritiene abbia, free
è il rimanente kb libero che l'heap ha per allocazioni aggiuntive pss
e priv dirty
sono gli stessi come discusso in precedenza specifico per le pagine associate a ciascuno dei cumuli.
Se vuoi solo guardare l'utilizzo della memoria in tutti i processi, puoi usare il comando adb shell procrank
. L'output di questo sullo stesso sistema è simile a:
PID Vss Rss Pss Utilizza cmdline 890 84456K 48668K 25850K 21284K server_sistema 1231 50748 K 39088 K 17587 K 13792 K 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 25728 K 25724 K 5774 K 3668 K zigote 977 24100K 24096K 5667K 4340K android.process.acore ... 59 336K 332K 99K 92K / system / bin / installd 60 396 K 392 K 93 K 84 K / sistema / bin / archivio chiavi 51 280K 276K 74K 68K / system / bin / servicemanager 54 256 K 252 K 69 K 64 K / sistema / bin / debuggerd
Qui le colonne Vss
e Rss
sono fondamentalmente rumorose (questi sono lo spazio degli indirizzi diretto e l'utilizzo della RAM di un processo, dove se sommi l'utilizzo della RAM attraverso i processi ottieni un numero ridicolmente grande).
Pss
è come abbiamo visto prima ed Uss
è Priv Dirty
.
Cosa interessante da notare qui: Pss
e Uss
sono leggermente (o più che leggermente) diversi da quelli che abbiamo visto meminfo
. Perché? Bene procrank usa un meccanismo del kernel diverso per raccogliere i suoi dati rispetto a loro meminfo
, e danno risultati leggermente diversi. Perché? Onestamente non ne ho idea. Credo che procrank
possa essere il più preciso ... ma in realtà, questo lascia solo il punto: "prendi qualsiasi informazione di memoria che ottieni con un granello di sale; spesso un granello molto grande".
Infine c'è il comando adb shell cat /proc/meminfo
che fornisce un riepilogo dell'utilizzo complessivo della memoria del sistema. Ci sono molti dati qui, solo i primi numeri che vale la pena discutere (e quelli rimanenti compresi da poche persone, e le mie domande su quelle poche persone su di loro spesso portano a spiegazioni contrastanti):
MemTotal: 395144 kB MemFree: 184936 kB Buffer: 880 kB Memoria cache: 84104 kB SwapCached: 0 kB
MemTotal
è la quantità totale di memoria disponibile per il kernel e lo spazio utente (spesso inferiore alla RAM fisica effettiva del dispositivo, poiché parte di tale RAM è necessaria per la radio, i buffer DMA, ecc.).
MemFree
è la quantità di RAM che non viene utilizzata affatto. Il numero che vedi qui è molto alto; in genere su un sistema Android questo sarebbe solo di pochi MB, poiché proviamo a utilizzare la memoria disponibile per mantenere i processi in esecuzione
Cached
è la RAM utilizzata per le cache del filesystem e altre cose simili. I sistemi tipici dovranno avere circa 20 MB per evitare di entrare in cattivi stati di paging; il killer di memoria esaurita di Android è ottimizzato per un particolare sistema per assicurarsi che i processi in background vengano interrotti prima che la RAM memorizzata nella cache venga consumata troppo da essi per provocare tale paging.
Sì, puoi ottenere informazioni sulla memoria a livello di codice e decidere se eseguire un lavoro intensivo in memoria.
Ottieni dimensioni heap VM chiamando:
Runtime.getRuntime().totalMemory();
Ottieni memoria VM allocata chiamando:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Ottieni il limite della dimensione dell'heap di VM chiamando:
Runtime.getRuntime().maxMemory()
Ottieni memoria allocata nativa chiamando:
Debug.getNativeHeapAllocatedSize();
Ho creato un'app per capire il comportamento OutOfMemoryError e monitorare l'utilizzo della memoria.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
Puoi ottenere il codice sorgente su https://github.com/coocood/oom-research
Questo è un work in progress, ma questo è ciò che non capisco:
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");
}
}
Perché il PID non è associato al risultato in activityManager.getProcessMemoryInfo ()? Chiaramente vuoi rendere significativi i dati risultanti, quindi perché Google ha reso così difficile correlare i risultati? Il sistema attuale non funziona nemmeno bene se voglio elaborare l'intero utilizzo della memoria poiché il risultato restituito è un array di oggetti android.os.Debug.MemoryInfo, ma nessuno di questi oggetti ti dice effettivamente a quali pid sono associati. Se passi semplicemente in una matrice di tutti i pid, non avrai modo di capire i risultati. A quanto ho capito, è inutile passare più di un pid alla volta e, in tal caso, perché farlo in modo che activityManager.getProcessMemoryInfo () prenda solo un array int?
Hackbod's è una delle migliori risposte su Stack Overflow. Illumina un soggetto molto oscuro. Mi ha aiutato molto.
Un'altra risorsa davvero utile è questo video da non perdere : Google I / O 2011: gestione della memoria per le app Android
AGGIORNARE:
Process Stats, un servizio per scoprire come la tua app gestisce la memoria spiegata nel post del blog Statistica del processo: capire come la tua app utilizza la RAM di Dianne Hackborn:
Android Studio 0.8.10+ ha introdotto uno strumento incredibilmente utile chiamato Memory Monitor .
A cosa serve:
- Mostra la memoria disponibile e utilizzata in un grafico e gli eventi di garbage collection nel tempo.
- Testare rapidamente se la lentezza dell'app potrebbe essere correlata a eventi eccessivi di garbage collection.
- Testare rapidamente se gli arresti anomali delle app possono essere correlati alla mancanza di memoria.
Figura 1. Forzare un evento GC (Garbage Collection) su Android Memory Monitor
Puoi avere molte buone informazioni sul consumo in tempo reale della RAM della tua app utilizzandolo.
1) Immagino di no, almeno non da Java.
2)
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
Abbiamo scoperto che tutti i modi standard per ottenere la memoria totale del processo corrente presentano alcuni problemi.
Runtime.getRuntime().totalMemory()
: restituisce solo la memoria JVMActivityManager.getMemoryInfo()
, Process.getFreeMemory()
E qualsiasi altra cosa in base a /proc/meminfo
- rendimenti informazioni di memoria su tutti i processi combinati (ad esempio android_util_Process.cpp )Debug.getNativeHeapAllocatedSize()
- utilizza le mallinfo()
informazioni che restituiscono informazioni sulle allocazioni di memoria eseguite esclusivamente dalle malloc()
funzioni correlate (vedi android_os_Debug.cpp )Debug.getMemoryInfo()
- fa il lavoro ma è troppo lento. Ci vogliono circa 200ms su Nexus 6 per una singola chiamata. L'overhead delle prestazioni rende questa funzione inutile per noi poiché la chiamiamo regolarmente e ogni chiamata è abbastanza evidente (vedi android_os_Debug.cpp )ActivityManager.getProcessMemoryInfo(int[])
- chiama Debug.getMemoryInfo()
internamente (vedi ActivityManagerService.java )Alla fine, abbiamo finito con il seguente codice:
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;
Restituisce la metrica VmRSS . Puoi trovare maggiori dettagli qui: uno , due e tre .
PS Ho notato che il tema presenta ancora una mancanza di un frammento di codice reale e semplice su come stimare l'utilizzo della memoria privata del processo se le prestazioni non sono un requisito critico:
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;
In Android Studio 3.0 hanno introdotto Android-Profiler per aiutarti a capire come la tua app utilizza CPU, memoria, rete e risorse della batteria.
https://developer.android.com/studio/profile/android-profiler
Ci sono molte risposte sopra che sicuramente ti aiuteranno ma (dopo 2 giorni di permessi e ricerche sugli strumenti di memoria adb) penso di poter aiutare anche con la mia opinione .
Come dice Hackbod: Quindi se dovessi prendere tutta la RAM fisica effettivamente mappata su ciascun processo e sommare tutti i processi, probabilmente finiresti con un numero molto maggiore della RAM totale effettiva. quindi non è possibile ottenere esattamente la quantità di memoria per processo.
Ma puoi avvicinarti ad esso con un po 'di logica..e io dirò come ..
Ci sono alcune API simili
android.os.Debug.MemoryInfo
eActivityManager.getMemoryInfo()
menzionate sopra di cui potresti già aver letto e usato, ma parlerò in altro modo
Quindi, prima di tutto devi essere un utente root per farlo funzionare. Entra nella console con il privilegio di root eseguendo il su
processo e ottieni il suo output and input stream
. Quindi passa id\n
( invio ) in ouputstream e scrivilo per elaborare l'output, se otterrà un inputstream contenente uid=0
, sei l'utente root.
Ora ecco la logica che userete nel processo sopra
Quando si arriva ouputstream del processo di passare tu comandi (procrank, dumpsys meminfo ecc ...), con \n
al posto di id e ottenere la sua inputstream
e leggere, memorizzare il flusso in byte [], char [] ecc .. utilizzare grezzo data..and voi sono fatti !!!!!
autorizzazione :
<uses-permission android:name="android.permission.FACTORY_TEST"/>
Controlla se sei un utente root:
// 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
}
}
Esegui il tuo comando con 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();
}
}
Ottieni dati grezzi in una singola stringa dalla console anziché in qualche istanza da qualsiasi API, che è complessa da archiviare poiché dovrai separarla manualmente .
Questo è solo un tentativo, per favore, suggeriscimi se ho perso qualcosa