Question Comment puis-je découvrir l'utilisation de la mémoire de mon application sous Android?


Comment puis-je trouver la mémoire utilisée sur mon application Android, par programmation?

J'espère qu'il y a un moyen de le faire. De plus, comment puis-je obtenir la mémoire gratuite du téléphone aussi?


745
2018-02-19 17:12


origine


Réponses:


Notez que l'utilisation de la mémoire sur les systèmes d'exploitation modernes comme Linux est un extrêmement zone compliquée et difficile à comprendre. En fait, les chances d'interpréter correctement les chiffres que vous obtenez sont extrêmement faibles. (Presque chaque fois que je regarde les numéros 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 qui ne donne qu'une vague conclusion.)

Note: nous avons maintenant beaucoup plus de documentation sur Gérer la mémoire de votre application cela couvre une grande partie du matériel ici et est plus à jour avec l'état d'Android.

La première chose est de lire probablement la dernière partie de cet article qui a une discussion sur la façon dont la mémoire est gérée sur Android:

Modifications de l'API de service à partir d'Android 2.0

À présent ActivityManager.getMemoryInfo() est notre API de plus haut niveau pour l'utilisation globale de la mémoire. Ceci est principalement là pour aider une application à mesurer à 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 peu utile, car la limite de tas Java est là en partie pour éviter qu'une application ne soit capable de stresser le système jusqu'à ce point.

En allant au niveau inférieur, vous pouvez utiliser l'API de débogage pour obtenir des informations au niveau du noyau sur l'utilisation de la mémoire: android.os.Debug.MemoryInfo

Remarque à partir de 2.0 il y a aussi une API, ActivityManager.getProcessMemoryInfo, pour obtenir cette information sur un autre processus: ActivityManager.getProcessMemoryInfo (int [])

Cela retourne 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 à savoir quelle est la différence entre Pss, PrivateDirty, et SharedDirty... Eh bien maintenant, le plaisir commence.

Beaucoup de mémoire dans Android (et les systèmes Linux en général) est réellement partagée entre plusieurs processus. La quantité de mémoire utilisée par un processus n'est donc pas vraiment claire. Ajouter au-dessus de cette pagination sur le disque (encore moins échanger que nous n'utilisons pas sur Android) et il est encore moins clair.

Ainsi, si vous deviez prendre en compte toute la RAM physique affectée à chaque processus et additionner tous les processus, vous obtiendriez probablement un nombre beaucoup plus grand que la RAM totale réelle.

le Pss number est une métrique calculée par le noyau qui prend en compte le partage de la mémoire. En fait, 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 à travers tous les processus pour voir la quantité totale de RAM qu'ils utilisent, et comparer pss entre les processus pour avoir une idée approximative de leur poids relatif.

L'autre métrique intéressante est ici PrivateDirty, qui est essentiellement la quantité de mémoire 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 cela est la RAM qui deviendra disponible pour le système lorsque ce processus disparaîtra (et probablement rapidement englobé dans des caches et d'autres utilisations de celui-ci).

C'est à peu près les API 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. Un commun est la commande adb shell dumpsys meminfo qui va cracher 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 virer sur le nom ou le pid d'un processus unique pour voir, par exemple adb shell dumpsys meminfo system donne moi le processus du système:

** MEMINFO au pid 890 [système] **
                    dalvik natif autre total
            taille: 10940 7047 N / A 17987
       attribué: 8943 5516 N / A 14459
            gratuit: 336 1531 so 1867
           (Pss): 4585 9282 11916 25783
  (sale sale): 2184 3596 916 6696
    (Priv sale): 4504 5956 7456 17916

 Objets
           Vues: 149 VoirRoots: 4
     AppContexte: 13 Activités: 0
          Atouts: 4 AssetManagers: 4
   Liants locaux: 141 Liants proxy: 158
Destinataires de la mort: 49
 OpenSSL Sockets: 0

 SQL
            tas: 205 dbFichiers: 0
       numPagers: 0 inactifPageKB: 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 des allocations réelles que tas pense avoir, free est le kb restant libre le tas a des allocations supplémentaires, et pss et priv dirty sont les mêmes que ceux qui ont été discutés auparavant pour les pages associées à chacun des tas.

Si vous voulez juste regarder 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 server_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 / système / bin / installd
   60 396K 392K 93K 84K / système / bin / keystore
   51 280K 276K 74K 68K / système / bin / servicemanager
   54 256K 252K 69K 64K / système / bin / debuggerd

Ici le Vss et Rss les colonnes sont essentiellement du bruit (ce sont l'espace d'adressage direct et l'utilisation de la RAM d'un processus, où si vous additionnez l'utilisation de la mémoire RAM à travers les processus, vous obtenez un nombre ridiculement grand).

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 recueillir ses données que meminfo fait, et ils donnent des résultats légèrement différents. Pourquoi donc? Honnêtement, je n'ai pas la moindre idée. Je crois procrank peut-être le plus précis ... mais vraiment, cela laisse juste le point: "prendre 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 cela 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 valent la peine d'être discutés (et ceux qui restent sont compris par quelques personnes, et mes questions sur ces quelques personnes à leur sujet entraînent souvent des explications contradictoires):

MemTotal: 395144 ko
MemFree: 184936 Ko
Tampons: 880 kB
Caché: 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, puisqu'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é; typiquement sur un système Android ce ne serait que quelques Mo, puisque nous essayons d'utiliser la mémoire disponible pour maintenir les processus en cours

Cached est la RAM utilisée pour les caches de systèmes de fichiers et autres choses de ce genre. Les systèmes typiques devront avoir 20 Mo ou plus pour éviter d'entrer dans de mauvais états de pagination; le tueur de mémoire hors de l'Android est réglé pour un système particulier afin de s'assurer que les processus d'arrière-plan sont détruits avant que la RAM mise en cache ne soit trop consommée par eux pour provoquer une telle pagination.


969
2018-02-19 21:44



Oui, vous pouvez obtenir des informations sur la mémoire par programme et décider d'effectuer un travail intensif en mémoire.

Obtenir la taille de segment VM en appelant:

Runtime.getRuntime().totalMemory();

Obtenir la mémoire VM allouée en appelant:

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

Obtenir la limite de taille de tas VM en appelant:

Runtime.getRuntime().maxMemory()

Obtenir la mémoire native affectée en appelant:

Debug.getNativeHeapAllocatedSize();

J'ai fait une application pour comprendre le comportement 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 à https://github.com/coocood/oom-research


69
2017-12-01 05:49



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 ()? Il est clair que vous voulez rendre les données résultantes significatives, alors pourquoi Google a-t-il rendu la corrélation des résultats si difficile? Le système actuel ne fonctionne même pas bien si je veux traiter toute l'utilisation de la mémoire puisque le résultat renvoyé est un tableau d'objets android.os.Debug.MemoryInfo, mais aucun de ces objets ne vous dit réellement à quels pids ils sont associés. Si vous passez simplement dans 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, et si c'est le cas, pourquoi faire en sorte que activityManager.getProcessMemoryInfo () ne prenne qu'un tableau int?


48
2017-08-13 18:32



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 à voir absolument: Google I / O 2011: gestion de la mémoire pour les applications Android


METTRE À JOUR:

Process Stats, un service pour découvrir comment votre application gère la mémoire expliqué sur le blog Process Stats: Comprendre comment votre application utilise la RAM par Dianne Hackborn:


24
2017-12-21 16:49



Android Studio 0.8.10+ a introduit un outil incroyablement utile appelé Moniteur de mémoire.

enter image description here

Qu'est-ce que c'est bon pour:

  • Affichage de la mémoire disponible et utilisée dans un graphique et de la récupération de place   événements au fil du temps.
  • Test rapide de la lenteur de l'application   liés à des événements excessifs de collecte des ordures.
  • Test rapide   si les plantages de l'application peuvent être liés au manque de mémoire.

enter image description here

Figure 1. Forcer un événement GC (Garbage Collection) sur Android Memory Monitor

Vous pouvez avoir beaucoup d'informations sur la consommation en temps réel de votre application en l'utilisant.


19
2017-08-12 12:40



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);

16
2018-02-19 17:31



Nous avons découvert que tous les moyens standard pour obtenir la mémoire totale du processus actuel ont quelques problèmes.

  • Runtime.getRuntime().totalMemory(): renvoie la mémoire JVM uniquement
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() et toute autre chose basée sur /proc/meminfo - renvoie des informations de mémoire sur tous les processus combinés (par ex. android_util_Process.cpp)
  • Debug.getNativeHeapAllocatedSize() - les usages mallinfo() qui renvoient des informations sur les allocations de mémoire effectuées par malloc() et fonctions connexes seulement (voir android_os_Debug.cpp)
  • Debug.getMemoryInfo() - fait le travail mais c'est trop lent. Ça prend à peu près 200ms sur Nexus 6 pour un seul appel. La surcharge de performance rend cette fonction inutile pour nous car nous l'appelons régulièrement et chaque appel est tout à fait perceptible (voir android_os_Debug.cpp)
  • ActivityManager.getProcessMemoryInfo(int[]) - appels Debug.getMemoryInfo() en interne (voir ActivityManagerService.java)

Finalement, 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;

Ça revient VmRSS métrique. Vous pouvez trouver plus de détails à ce sujet ici: un, deux et Trois.


P.S. J'ai remarqué que le thème a toujours un manque d'un extrait de code réel et simple de la façon de estimation l'utilisation de la mémoire privée du processus si la performance n'est 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;

2
2017-08-14 02:02