Comment trouver la mémoire utilisée sur mon application Android, par programme?
J'espère qu'il y a un moyen de le faire. De plus, comment puis-je également obtenir la mémoire libre du téléphone?
Comment trouver la mémoire utilisée sur mon application Android, par programme?
J'espère qu'il y a un moyen de le faire. De plus, comment puis-je également obtenir la mémoire libre du téléphone?
Réponses:
Notez que l'utilisation de la mémoire sur des systèmes d'exploitation modernes comme Linux est un domaine extrêmement compliqué et difficile à comprendre. En fait, les chances que vous interprétiez correctement les chiffres que vous obtenez sont extrêmement faibles. (À peu près chaque fois que je regarde les chiffres d'utilisation de la mémoire avec d'autres ingénieurs, il y a toujours une longue discussion sur ce qu'ils signifient réellement, ce qui ne donne qu'une vague conclusion.)
Remarque: nous avons maintenant une documentation beaucoup plus complète sur la gestion de la mémoire de votre application qui couvre une grande partie du matériel ici et est plus à jour avec l'état d'Android.
La première chose est probablement de lire la dernière partie de cet article qui explique comment la mémoire est gérée sur Android:
Modifications de l'API de service à partir d'Android 2.0
Voici maintenant ActivityManager.getMemoryInfo()
notre API de plus haut niveau pour examiner l'utilisation globale de la mémoire. Ceci est principalement là pour aider une application à évaluer à quel point le système arrive à ne plus avoir de mémoire pour les processus d'arrière-plan, ce qui nécessite de commencer à tuer les processus nécessaires comme les services. Pour les applications Java pures, cela devrait être de peu d'utilité, car la limite de tas Java est là en partie pour éviter qu'une application ne soit en mesure de stresser le système à ce stade.
À un niveau inférieur, vous pouvez utiliser l'API de débogage pour obtenir des informations brutes au niveau du noyau sur l'utilisation de la mémoire: android.os.Debug.MemoryInfo
Notez ActivityManager.getProcessMemoryInfo
qu'à partir de la version 2.0, il existe également une API, pour obtenir ces informations sur un autre processus: ActivityManager.getProcessMemoryInfo (int [])
Cela renvoie une structure MemoryInfo de bas niveau avec toutes ces données:
/** 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;
Mais quant à ce que la différence entre Pss
, PrivateDirty
et SharedDirty
... eh bien maintenant le plaisir commence.
Une grande partie de la mémoire dans Android (et les systèmes Linux en général) est en fait partagée entre plusieurs processus. La quantité de mémoire utilisée par un processus n'est donc pas claire. Ajoutez à cela la pagination sur le disque (et encore moins l'échange que nous n'utilisons pas sur Android) et c'est encore moins clair.
Ainsi, si vous deviez prendre toute la RAM physique réellement mappée dans chaque processus et additionner tous les processus, vous vous retrouveriez probablement avec un nombre beaucoup plus élevé que la RAM totale réelle.
Le Pss
nombre est une métrique calculée par le noyau qui prend en compte le partage de mémoire - fondamentalement, chaque page de RAM dans un processus est mise à l'échelle par un ratio du nombre d'autres processus utilisant également cette page. De cette façon, vous pouvez (en théorie) additionner le pss sur tous les processus pour voir la RAM totale qu'ils utilisent et comparer le pss entre les processus pour avoir une idée approximative de leur poids relatif.
L'autre métrique intéressante ici est PrivateDirty
, qui est fondamentalement la quantité de RAM à l'intérieur du processus qui ne peut pas être paginée sur le disque (elle n'est pas sauvegardée par les mêmes données sur le disque), et n'est partagée avec aucun autre processus. Une autre façon de voir les choses est la RAM qui deviendra disponible pour le système lorsque ce processus disparaîtra (et sera probablement rapidement intégrée dans les caches et autres utilisations).
C'est à peu près les API du SDK pour cela. Cependant, vous pouvez faire plus en tant que développeur avec votre appareil.
En utilisant adb
, il y a beaucoup d'informations que vous pouvez obtenir sur l'utilisation de la mémoire d'un système en cours d'exécution. Une commande courante est la commande adb shell dumpsys meminfo
qui crachera un tas d'informations sur l'utilisation de la mémoire de chaque processus Java, contenant les informations ci-dessus ainsi qu'une variété d'autres choses. Vous pouvez également mettre le nom ou le pid d'un seul processus à voir, par exemple, adb shell dumpsys meminfo system
donnez-moi le processus système:
** MEMINFO dans pid 890 [système] ** dalvik natif autre total taille: 10940 7047 N / A 17987 attribué: 8943 5516 N / A 14459 gratuit: 336 1531 N / A 1867 (Pss): 4585 9282 11916 25783 (partagé sale): 2184 3596 916 6696 (privé sale): 4504 5956 7456 17916 Objets Vues: 149 VuesRacines: 4 AppContexts: 13 Activités: 0 Actifs: 4 AssetManagers: 4 Liants locaux: 141 Liants proxy: 158 Destinataires du décès: 49 Sockets OpenSSL: 0 SQL tas: 205 dbFichiers: 0 numPagers: 0 inactivePageKB: 0 activePageKB: 0
La section supérieure est la principale, où size
est la taille totale dans l'espace d'adressage d'un tas particulier, allocated
est le kb d'allocations réelles que le tas pense avoir, free
est le kb restant libre que le tas a pour des allocations supplémentaires, et pss
et priv dirty
sont les mêmes comme discuté avant spécifique aux pages associées à chacun des tas.
Si vous souhaitez simplement examiner l'utilisation de la mémoire dans tous les processus, vous pouvez utiliser la commande adb shell procrank
. La sortie de ceci sur le même système ressemble à:
PID Vss Rss Pss Uss cmdline 890 84456K 48668K 25850K 21284K serveur_système 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 / système / bac / magasin de clés 51 280K 276K 74K 68K / système / bac / gestionnaire de service 54 256K 252K 69K 64K / system / bin / debuggerd
Ici, les colonnes Vss
et Rss
sont essentiellement du bruit (ce sont l'espace d'adressage simple et l'utilisation de la RAM d'un processus, où si vous additionnez l'utilisation de la RAM entre les processus, vous obtenez un nombre ridiculement élevé).
Pss
est comme nous l'avons vu auparavant, et Uss
est Priv Dirty
.
Chose intéressante à noter ici: Pss
et Uss
sont légèrement (ou plus que légèrement) différents de ce que nous avons vu meminfo
. Pourquoi donc? Eh bien, procrank utilise un mécanisme de noyau différent pour collecter ses données meminfo
, et ils donnent des résultats légèrement différents. Pourquoi donc? Honnêtement, je n'ai aucune idée. Je crois que c'est procrank
peut-être le plus précis ... mais vraiment, cela laisse juste le point: "prenez toutes les informations de mémoire que vous obtenez avec un grain de sel; souvent un très gros grain."
Enfin, il y a la commande adb shell cat /proc/meminfo
qui donne un résumé de l'utilisation globale de la mémoire du système. Il y a beaucoup de données ici, seuls les premiers chiffres méritent d'être discutés (et les autres sont compris par peu de gens, et mes questions à ces quelques personnes à leur sujet entraînent souvent des explications contradictoires):
MemTotal: 395144 kB MemFree: 184936 kB Tampons: 880 kB En cache: 84104 kB SwapCached: 0 ko
MemTotal
est la quantité totale de mémoire disponible pour le noyau et l'espace utilisateur (souvent inférieure à la RAM physique réelle du périphérique, car une partie de cette RAM est nécessaire pour la radio, les tampons DMA, etc.).
MemFree
est la quantité de RAM qui n'est pas utilisée du tout. Le nombre que vous voyez ici est très élevé; généralement sur un système Android, cela ne représente que quelques Mo, car nous essayons d'utiliser la mémoire disponible pour maintenir les processus en cours
Cached
est la RAM utilisée pour les caches du système de fichiers et d'autres choses de ce type. Les systèmes typiques devront avoir environ 20 Mo pour éviter d’entrer dans de mauvais états de pagination; Android tueur de mémoire est réglé pour un système particulier afin de s'assurer que les processus d'arrière-plan sont supprimés avant que la RAM mise en cache ne soit trop consommée par eux pour entraîner une telle pagination.
Oui, vous pouvez obtenir des informations sur la mémoire par programme et décider d'effectuer un travail gourmand en mémoire.
Obtenez VM Heap Size en appelant:
Runtime.getRuntime().totalMemory();
Obtenez la mémoire VM allouée en appelant:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Obtenez VM Heap Size Limit en appelant:
Runtime.getRuntime().maxMemory()
Obtenez la mémoire allouée native en appelant:
Debug.getNativeHeapAllocatedSize();
J'ai créé une application pour comprendre le comportement de OutOfMemoryError et surveiller l'utilisation de la mémoire.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
Vous pouvez obtenir le code source sur https://github.com/coocood/oom-research
C'est un travail en cours, mais c'est ce que je ne comprends pas:
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");
}
}
Pourquoi le PID n'est-il pas mappé au résultat dans activityManager.getProcessMemoryInfo ()? De toute évidence, vous voulez rendre les données résultantes significatives, alors pourquoi Google a-t-il rendu si difficile la corrélation des résultats? Le système actuel ne fonctionne même pas bien si je veux traiter l'intégralité de l'utilisation de la mémoire car le résultat renvoyé est un tableau d'objets android.os.Debug.MemoryInfo, mais aucun de ces objets ne vous indique réellement à quels pids ils sont associés. Si vous passez simplement un tableau de tous les pids, vous n'aurez aucun moyen de comprendre les résultats. Si je comprends bien son utilisation, cela rend inutile de passer plus d'un pid à la fois, puis si c'est le cas, pourquoi faire en sorte que activityManager.getProcessMemoryInfo () ne prenne qu'un tableau int?
Hackbod's est l'une des meilleures réponses sur Stack Overflow. Il éclaire un sujet très obscur. Cela m'a beaucoup aidé.
Une autre ressource vraiment utile est cette vidéo incontournable: Google I / O 2011: Gestion de la mémoire pour les applications Android
MISE À JOUR:
Process Stats, un service pour découvrir comment votre application gère la mémoire expliqué dans l'article de blog Process Stats: Comprendre comment votre application utilise la RAM par Dianne Hackborn:
Android Studio 0.8.10+ a introduit un outil incroyablement utile appelé Memory Monitor .
À quoi ça sert:
- Affichage de la mémoire disponible et utilisée dans un graphique et des événements de récupération de place dans le temps.
- Tester rapidement si la lenteur de l'application peut être liée à des événements de collecte de déchets excessifs.
- Tester rapidement si les plantages d'applications peuvent être liés à un manque de mémoire.
Figure 1. Forcer un événement GC (Garbage Collection) sur Android Memory Monitor
Vous pouvez avoir beaucoup de bonnes informations sur la consommation de RAM en temps réel de votre application en l'utilisant.
1) Je suppose que non, du moins pas de Java.
2)
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
Nous avons découvert que toutes les méthodes standard pour obtenir la mémoire totale du processus actuel ont quelques problèmes.
Runtime.getRuntime().totalMemory()
: renvoie uniquement la mémoire JVMActivityManager.getMemoryInfo()
, Process.getFreeMemory()
et tout autre élément basé sur /proc/meminfo
- renvoie des informations sur la mémoire de tous les processus combinés (par exemple android_util_Process.cpp )Debug.getNativeHeapAllocatedSize()
- utilisations mallinfo()
qui retournent des informations sur les allocations de mémoire effectuées par malloc()
et les fonctions associées uniquement (voir android_os_Debug.cpp )Debug.getMemoryInfo()
- fait le boulot mais c'est trop lent. Cela prend environ 200 ms sur le Nexus 6 pour un seul appel. La surcharge de performances rend cette fonction inutile pour nous car nous l'appelons régulièrement et chaque appel est assez perceptible (voir android_os_Debug.cpp )ActivityManager.getProcessMemoryInfo(int[])
- appels en Debug.getMemoryInfo()
interne (voir ActivityManagerService.java )Enfin, nous avons fini par utiliser le code suivant:
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;
Il renvoie la métrique VmRSS . Vous pouvez trouver plus de détails à ce sujet ici: un , deux et trois .
PS J'ai remarqué que le thème manque toujours d'un extrait de code réel et simple sur la façon d' estimer l'utilisation de la mémoire privée du processus si les performances ne sont pas une exigence critique:
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;
Dans Android Studio 3.0, ils ont introduit Android-profiler pour vous aider à comprendre comment votre application utilise les ressources CPU, mémoire, réseau et batterie.
https://developer.android.com/studio/profile/android-profiler
Il y a beaucoup de réponses ci-dessus qui vous aideront certainement, mais (après 2 jours de moyens et de recherches sur les outils de mémoire adb), je pense que je peux aussi aider avec mon opinion .
Comme le dit Hackbod: Ainsi, si vous deviez prendre toute la RAM physique réellement mappée dans chaque processus et additionner tous les processus, vous vous retrouveriez probablement avec un nombre beaucoup plus élevé que la RAM totale réelle. il n'y a donc aucun moyen d'obtenir la quantité exacte de mémoire par processus.
Mais vous pouvez vous en approcher par une logique .. et je vais vous dire comment ..
Il y a des API comme
android.os.Debug.MemoryInfo
etActivityManager.getMemoryInfo()
mentionnées ci-dessus que vous pourriez déjà avoir lues et utilisées, mais je parlerai d'une autre manière
Donc, tout d'abord, vous devez être un utilisateur root pour le faire fonctionner. Accédez à la console avec le privilège root en exécutant su
in process et obtenez son output and input stream
. Ensuite, passez id\n
(entrez) dans ouputstream et écrivez-le pour traiter la sortie, si vous obtiendrez un flux d'entrée contenant uid=0
, vous êtes l'utilisateur root.
Voici maintenant la logique que vous utiliserez dans le processus ci-dessus
Lorsque vous obtenez ouputstream de processus passez votre commande (procrank, dumpsys meminfo etc ...) avec \n
au lieu de id et obtenez son inputstream
et lisez, stockez le flux en octets [], char [] etc. utilisez des données brutes .. et vous sont fait!!!!!
autorisation :
<uses-permission android:name="android.permission.FACTORY_TEST"/>
Vérifiez si vous êtes 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
}
}
Exécutez votre commande avec 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();
}
}
Vous obtenez des données brutes dans une seule chaîne à partir de la console au lieu dans certains cas de n'importe quelle API, ce qui est complexe à stocker car vous devrez les séparer manuellement .
Ceci est juste un essai, veuillez me suggérer si j'ai raté quelque chose