824 votes

Comment 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, de manière programmatique ?

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

2 votes

Ou si quelqu'un veut en savoir plus sur tout cela, veuillez vous référer à http://elinux.org/Android_Memory_Usage

1 votes

Gérer la mémoire de votre application developer.Android.com/topic/performance/memory

1031voto

hackbod Points 55292

Notez que l'utilisation de la mémoire sur les systèmes d'exploitation modernes comme Linux est une extrêmement domaine compliqué et difficile à comprendre. En fait, les chances que vous interprétiez correctement les chiffres que vous obtenez sont extrêmement faibles. (Pratiquement chaque fois que j'examine 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 qui ne débouche que sur une vague conclusion).

Note : nous disposons désormais d'une documentation beaucoup plus complète sur 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 à faire est probablement de lire la dernière partie de cet article qui traite de la manière dont la mémoire est gérée sur Android :

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

Maintenant ActivityManager.getMemoryInfo() est notre API de plus haut niveau pour examiner l'utilisation globale de la mémoire. Elle est principalement destinée à aider une application à évaluer dans quelle mesure le système est sur le point de ne plus avoir de mémoire pour les processus d'arrière-plan, et donc de devoir commencer à tuer les processus nécessaires comme les services. Pour les applications purement Java, cela ne devrait pas être très utile, puisque la limite du tas Java est là en partie pour éviter qu'une application puisse stresser le système à ce point.

En allant plus bas, 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 qu'à partir de la version 2.0, il existe également une API, ActivityManager.getProcessMemoryInfo pour obtenir cette information 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 à la différence entre Pss , PrivateDirty y SharedDirty ... et maintenant le plaisir commence.

Une grande partie de la mémoire d'Android (et des systèmes Linux en général) est en fait partagée entre plusieurs processus. Ainsi, la quantité de mémoire utilisée par un processus n'est pas vraiment claire. Si l'on ajoute à cela la pagination sur le disque (sans parler du swap que nous n'utilisons pas sous Android), la situation est encore moins claire.

Ainsi, si vous deviez prendre toute la mémoire vive physique réellement affectée à chaque processus et additionner tous les processus, vous obtiendriez probablement un nombre bien supérieur à la mémoire vive totale réelle.

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

L'autre mesure intéressante ici est PrivateDirty Il s'agit essentiellement de 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 qui n'est pas partagée avec d'autres 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 probablement rapidement subsumée dans les caches et autres utilisations).

C'est à peu près l'API du SDK pour cela. Cependant, en tant que développeur, vous pouvez faire plus avec votre appareil.

Utilisation de adb il y a beaucoup d'informations que vous pouvez obtenir sur l'utilisation de la mémoire d'un système en fonctionnement. Un exemple courant est la commande adb shell dumpsys meminfo qui fournira 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 aussi ajouter le nom ou le pid d'un seul processus pour voir, par exemple adb shell dumpsys meminfo system donnez-moi le processus du système :

\*\* MEMINFO in pid 890 \[system\] \*\*
                    native   dalvik    other    total
            size:    10940     7047      N/A    17987
       allocated:     8943     5516      N/A    14459
            free:      336     1531      N/A     1867
           (Pss):     4585     9282    11916    25783
  (shared dirty):     2184     3596      916     6696
    (priv dirty):     4504     5956     7456    17916

 Objects
           Views:      149        ViewRoots:        4
     AppContexts:       13       Activities:        0
          Assets:        4    AssetManagers:        4
   Local Binders:      141    Proxy Binders:      158
Death Recipients:       49
 OpenSSL Sockets:        0

 SQL
            heap:      205          dbFiles:        0
       numPagers:        0   inactivePageKB:        0
    activePageKB:        0

La section supérieure est la principale, où size est la taille totale de l'espace d'adressage d'un tas particulier, allocated est le kb d'allocations réelles que le tas pense avoir, free est le nombre de kb restants libres dans le tas pour des allocations supplémentaires, et pss y priv dirty sont les mêmes que ceux discutés précédemment, spécifiques aux pages associées à chacun des tas.

Si vous voulez simplement regarder l'utilisation de la mémoire pour tous les processus, vous pouvez utiliser la commande adb shell procrank . Le résultat de cette opération sur le même système ressemble à ceci :

  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/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

Ici, le Vss y Rss sont essentiellement du bruit (il s'agit de l'espace d'adressage et de 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 grand).

Pss est comme nous l'avons vu auparavant, et Uss es Priv Dirty .

Une chose intéressante à noter ici : Pss y Uss sont légèrement (ou plus que légèrement) différents de ceux que nous avons vus en meminfo . Pourquoi ? Eh bien procrank utilise un mécanisme de noyau différent pour collecter ses données par rapport à meminfo le fait, et ils donnent des résultats légèrement différents. Pourquoi cela ? Honnêtement, je n'en ai pas la moindre idée. Je crois procrank est peut-être le plus précis... mais en fait, cela ne fait que laisser le point : "prenez toute information sur la 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 valent la peine d'être discutés (et les autres sont compris par peu de personnes, et mes questions à ces quelques personnes à leur sujet aboutissent souvent à des explications contradictoires) :

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

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, il ne s'agit que de quelques Mo, puisque nous essayons d'utiliser la mémoire disponible pour faire tourner les processus.

Cached est la RAM utilisée pour les caches des systèmes de fichiers et autres. Les systèmes typiques ont besoin d'environ 20 Mo pour éviter les états de pagination. Le tueur de mémoire d'Android est réglé pour un système particulier afin de s'assurer que les processus d'arrière-plan sont tués avant que la mémoire vive en cache ne soit trop consommée par eux pour entraîner une telle pagination.

1 votes

Jetez un coup d'œil à pixelbeat.org/scripts/ps_mem.py qui utilise les techniques mentionnées ci-dessus pour montrer la RAM utilisée pour les programmes

18 votes

Très bien écrit ! J'ai écrit un article sur la gestion de la mémoire et l'utilisation de différents outils pour inspecter l'utilisation du tas ici. macgyverdev.blogspot.com/2011/11/ si quelqu'un le trouve utile.

3 votes

Que sont exactement les deux colonnes "dalvik" et "native" ?

82voto

coocood Points 616

Oui, vous pouvez obtenir des informations sur la mémoire de manière programmatique et décider d'effectuer ou non un travail nécessitant beaucoup de mémoire.

Obtenir la taille du tas de la 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 la taille du tas de la VM en appelant :

Runtime.getRuntime().maxMemory()

Obtenir la mémoire native allouée 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 à l'adresse suivante https://github.com/coocood/oom-research

7 votes

Ce runtime renvoie-t-il l'utilisation de la mémoire par le processus en cours ou par le tas global du système ?

1 votes

@mahemadhi de la JavaDoc de la méthode totalMemory() "Renvoie la quantité totale de mémoire disponible pour le programme en cours d'exécution".

1 votes

Ce n'est pas une réponse correcte à la question. La réponse ne porte pas sur une application spécifique.

52voto

Ryan Beesley Points 391

C'est un travail en cours, mais voici 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 associé au résultat dans activityManager.getProcessMemoryInfo() ? Il est clair que vous voulez que les données résultantes soient 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 la totalité de l'utilisation de la mémoire puisque le résultat retourné 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 dans un tableau de tous les pids, vous n'aurez aucun moyen de comprendre les résultats. Si je comprends son utilisation, il est inutile de passer plus d'un pid à la fois, et si c'est le cas, pourquoi faire en sorte que activityManager.getProcessMemoryInfo() prenne seulement un tableau d'int ?

2 votes

Ils sont probablement dans le même ordre que le tableau d'entrée.

2 votes

Cela semble être une façon très peu intuitive de faire les choses. Oui, c'est probablement le cas, mais en quoi est-ce une façon de faire de la POO ?

6 votes

L'API a été conçue pour l'efficacité, et non pour la facilité d'utilisation ou la simplicité. Ce n'est tout simplement pas quelque chose que 99 % des applications devraient toucher, l'efficacité est donc l'objectif de conception le plus important.

24voto

Xavi Gil Points 4088

La réponse de Hackbod est l'une des meilleures réponses sur Stack Overflow. Elle fait la lumière sur un sujet très obscur. Elle m'a beaucoup aidé.

Une autre ressource très utile est cette vidéo à voir absolument : Google I/O 2011 : Gestion de la mémoire pour les applications Android


UPDATE :

Process Stats, un service permettant de découvrir comment votre application gère la mémoire, expliqué dans l'article du blogue Statistiques des processus : Comprendre comment votre application utilise la RAM par Dianne Hackborn :

17voto

yanchenko Points 24142

1) Je ne pense pas, du moins pas à partir de Java.
2)

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

2 votes

Changer en (ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE) ;) au lieu de (ActivityManager activityManager = = (ActivityManager) getSystemService(ACTIVITY_SERVICE) ;)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X