149 votes

Détecter la taille du tas d'une application sous Android

Comment détecter de manière programmatique la taille du tas d'application disponible pour une application Android ?

J'ai entendu dire qu'il y a une fonction qui fait ça dans les versions ultérieures du SDK. Dans tous les cas, je cherche une solution qui fonctionne pour les versions 1.5 et suivantes.

2 votes

456voto

Carl Points 5553

Il y a deux façons de penser à votre phrase "taille disponible du tas de l'application" :

  1. Combien de tas de données mon application peut-elle utiliser avant qu'une erreur grave ne soit déclenchée ? Et

  2. Combien de tas debe l'utilisation de mon application, compte tenu des contraintes de la version du système d'exploitation Android et du matériel de l'appareil de l'utilisateur ?

Il existe une méthode différente pour déterminer chacun des éléments ci-dessus.

Pour le point 1 ci-dessus : maxMemory()

qui peut être invoqué (par exemple, dans la section de votre activité principale onCreate() ) comme suit :

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

Cette méthode vous indique le nombre total de octets de la pile de votre application est autorisé à utiliser.

Pour le point 2 ci-dessus : getMemoryClass()

qui peut être invoqué comme suit :

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

Cette méthode vous indique approximativement combien de mégaoctets du tas de votre application debe si elle veut respecter les limites de l'appareil actuel et le droit des autres applications de fonctionner sans être forcées de façon répétée dans la zone d'ombre. onStop() / onResume() cycle alors qu'ils sont brutalement expulsés de la mémoire pendant que votre application éléphantesque prend un bain dans le jacuzzi d'Android.

Cette distinction n'est pas clairement documentée, pour autant que je sache, mais j'ai testé cette hypothèse sur cinq appareils Android différents (voir ci-dessous) et j'ai confirmé à ma satisfaction que cette interprétation est correcte.

Pour une version stock d'Android, maxMemory() retournera typiquement le même nombre de mégaoctets que ceux indiqués dans getMemoryClass() (c'est-à-dire environ un million de fois cette dernière valeur).

La seule situation (dont j'ai connaissance) pour laquelle les deux méthodes peuvent diverger est sur un appareil enraciné fonctionnant sous une version d'Android telle que CyanogenMod, qui permet à l'utilisateur de manuellement sélectionnez la taille du tas autorisée pour chaque application. Dans CM, par exemple, cette option apparaît sous "CyanogenMod settings" / "Performance" / "VM heap size".

REMARQUE : Sachez que le réglage manuel de cette valeur peut perturber votre système, en particulier si vous sélectionnez une valeur inférieure à la normale pour votre appareil.

Voici les résultats de mon test montrant les valeurs retournées par maxMemory() y getMemoryClass() pour quatre appareils différents fonctionnant sous CyanogenMod, en utilisant deux valeurs de tas différentes (définies manuellement) pour chacun :

  • G1 :
    • La taille du tas de la VM étant fixée à 16 Mo :
      • Mémoire maximale : 16777216
      • getMemoryClass : 16
    • Avec la taille du tas de la VM fixée à 24MB :
      • maxMemory : 25165824
      • getMemoryClass : 16
  • Moto Droid :
    • Avec la taille du tas de la VM fixée à 24MB :
      • maxMemory : 25165824
      • getMemoryClass : 24
    • Avec la taille du tas de la VM fixée à 16 Mo :
      • Mémoire maximale : 16777216
      • getMemoryClass : 24
  • Nexus One :
    • La taille du tas de la VM étant fixée à 32 Mo :
      • maxMemory : 33554432
      • getMemoryClass : 32
    • La taille du tas de la VM étant fixée à 24 Mo :
      • maxMemory : 25165824
      • getMemoryClass : 32
  • Viewsonic GTab :
    • Avec la taille du tas de la VM fixée à 32 :
      • maxMemory : 33554432
      • getMemoryClass : 32
    • Avec la taille du tas de la VM fixée à 64 :
      • Mémoire maximale : 67108864
      • getMemoryClass : 32

En plus de ce qui précède, j'ai testé sur une tablette Novo7 Paladin fonctionnant avec Ice Cream Sandwich. Il s'agissait essentiellement d'une version stock d'ICS, sauf que j'ai enraciné la tablette par un processus simple qui ne remplace pas l'ensemble du système d'exploitation, et en particulier ne fournit pas une interface qui permettrait d'ajuster manuellement la taille du tas.

Pour cet appareil, voici les résultats :

  • Novo7
    • Mémoire maximale : 62914560
    • getMemoryClass : 60

Aussi (par Kishore dans un commentaire ci-dessous) :

  • HTC One X
    • Mémoire maximale : 67108864
    • getMemoryClass : 64

Et (selon le commentaire d'akauppi) :

  • Samsung Galaxy Core Plus
    • maxMemory : (Non spécifié dans le commentaire)
    • getMemoryClass : 48
    • largeMemoryClass : 128

Par un commentaire de cmcromance :

  • Galaxy S3 (Jelly Bean) grand tas
    • Mémoire maximale : 268435456
    • getMemoryClass : 64

Et (selon les commentaires de Tencent) :

  • LG Nexus 5 (4.4.3) normal
    • Mémoire maximale : 201326592
    • getMemoryClass : 192
  • LG Nexus 5 (4.4.3) gros tas de données
    • maxMemory : 536870912
    • getMemoryClass : 192
  • Galaxy Nexus (4.3) normal
    • Mémoire maximale : 100663296
    • getMemoryClass : 96
  • Galaxy Nexus (4.3) gros tas de données
    • Mémoire maximale : 268435456
    • getMemoryClass : 96
  • Galaxy S4 Play Store Edition (4.4.2) normal
    • Mémoire maximale : 201326592
    • getMemoryClass : 192
  • Galaxy S4 Play Store Edition (4.4.2) grand tas
    • maxMemory : 536870912
    • getMemoryClass : 192

Autres dispositifs

  • Huawei Nexus 6P (6.0.1) normal
    • Mémoire maximale : 201326592
    • getMemoryClass : 192

Je n'ai pas testé ces deux méthodes en utilisant l'option spéciale du manifeste Android:largeHeap="true" disponible depuis Honeycomb, mais grâce à cmcromance et tencent, nous disposons de quelques exemples de valeurs largeHeap, comme indiqué ci-dessus.

Mon attente (qui semble être soutenue par les chiffres de largeHeap ci-dessus) serait que cette option aurait un effet similaire à celui de la configuration manuelle du tas via un OS enraciné - c'est-à-dire qu'elle augmenterait la valeur de maxMemory() tout en laissant getMemoryClass() seul. Il existe une autre méthode, getLargeMemoryClass(), qui indique la quantité de mémoire autorisée pour une application utilisant le paramètre largeHeap. La documentation de getLargeMemoryClass() indique que "la plupart des applications ne devraient pas avoir besoin de cette quantité de mémoire et devraient plutôt respecter la limite fixée par getMemoryClass()".

Si j'ai bien compris, l'utilisation de cette option présenterait les mêmes avantages (et inconvénients) que l'utilisation de l'espace libéré par un utilisateur qui a augmenté le tas via un système d'exploitation enraciné (c'est-à-dire que si votre application utilise la mémoire supplémentaire, elle ne sera probablement pas aussi agréable à utiliser que les autres applications que l'utilisateur exécute en même temps).

Notez que la classe de mémoire ne doit apparemment pas être un multiple de 8 Mo.

Nous pouvons voir d'après ce qui précède que le getMemoryClass() Le résultat est immuable pour une configuration donnée du dispositif/OS, tandis que la valeur de maxMemory() change lorsque le tas est défini différemment par l'utilisateur.

Ma propre expérience pratique est que sur le G1 (qui a une classe de mémoire de 16), si je sélectionne manuellement 24MB comme taille du tas, je peux fonctionner sans erreur même lorsque mon utilisation de la mémoire est autorisée à dériver vers 20MB (on peut supposer que cela pourrait aller jusqu'à 24MB, bien que je n'aie pas essayé). Mais d'autres applications de taille similaire peuvent être vidées de leur mémoire en raison de la porosité de ma propre application. Et, inversement, mon peut être vidée de sa mémoire si ces autres applications à maintenance élevée sont amenées au premier plan par l'utilisateur.

Ainsi, vous ne pouvez pas dépasser la quantité de mémoire spécifiée par l'option maxMemory() . Et, vous devriez essayez pour rester dans les limites spécifiées par getMemoryClass() . L'un des moyens d'y parvenir, si tout le reste échoue, pourrait être de limiter les fonctionnalités de ces dispositifs de manière à économiser la mémoire.

Enfin, si vous prévoyez de dépasser le nombre de mégaoctets spécifié dans la section getMemoryClass() je vous conseille de travailler longuement et sérieusement sur la sauvegarde et la restauration de l'état de votre application, afin que l'expérience de l'utilisateur soit pratiquement ininterrompue en cas d'incident. onStop() / onResume() se produit.

Dans mon cas, pour des raisons de performances, je limite mon application aux appareils fonctionnant sous la version 2.2 et plus, ce qui signifie que presque tous les appareils utilisant mon application auront une classe de mémoire de 24 ou plus. Je peux donc concevoir mon application pour qu'elle occupe jusqu'à 20 Mo de tas et avoir la certitude qu'elle sera compatible avec les autres applications que l'utilisateur peut exécuter en même temps.

Mais il y aura toujours quelques utilisateurs enracinés qui ont chargé une version 2.2 ou supérieure d'Android sur un appareil plus ancien (par exemple, un G1). Lorsque vous rencontrez une telle configuration, vous devez idéalement réduire votre utilisation de la mémoire, même si maxMemory() vous dit que vous pouvez aller beaucoup plus loin que les 16 Mo que l'on vous propose. getMemoryClass() vous dit que vous debe cible. Et si vous ne pouvez pas garantir de manière fiable que votre application respectera ce budget, assurez-vous au moins que onStop() / onResume() fonctionne sans problème.

getMemoryClass() comme l'indique Diane Hackborn (hackbod) ci-dessus, n'est disponible que jusqu'au niveau 5 de l'API (Android 2.0), et donc, comme elle le conseille, vous pouvez supposer que le matériel physique de tout appareil exécutant une version antérieure du système d'exploitation est conçu pour prendre en charge de manière optimale les applications occupant un espace de tas de 16 Mo maximum.

En revanche, maxMemory() d'après la documentation, est disponible jusqu'au niveau 1 de l'API. maxMemory() sur une version antérieure à la version 2.0, renverra probablement une valeur de 16 Mo, mais j'ai faire Je constate que dans mes versions (beaucoup plus récentes) de CyanogenMod, l'utilisateur peut sélectionner une valeur de tas aussi basse que 12 Mo, ce qui entraînerait vraisemblablement une limite de tas inférieure. maxMemory() même pour les versions du système d'exploitation antérieures à la version 2.0. Il se peut même que vous deviez refuser de fonctionner dans le cas improbable où cette valeur est fixée à moins de 16 Mo, si vous avez besoin d'avoir plus de 10 Mo de mémoire. maxMemory() indique est autorisé.

2 votes

@Carl Bonjour Carl, Merci pour votre excellent post. Et j'ai testé l'option, Android:largeHeap="true" sur le Galaxy S3 avec JellyBean. Voici le résultat. [maxMemory:256.0MB , memoryClass:64MB] (maxMemory était de 64MB sans l'option largeheap)

0 votes

@cmcromance : Merci pour ces informations ! J'ai intégré vos résultats, avec crédit, dans le texte de mon post.

1 votes

Carl Haha C'est un grand honneur ! :)

20voto

hackbod Points 55292

L'officiel API est :

Ceci a été introduit dans la version 2.0 où des dispositifs de mémoire plus grands sont apparus. Vous pouvez supposer que les périphériques exécutant des versions antérieures du système d'exploitation utilisent la classe de mémoire originale (16).

13voto

Neil Traft Points 6844

Debug.getNativeHeapSize() fera l'affaire, je pense. C'est là depuis la version 1.0, cependant.

El Debug dispose d'un grand nombre de méthodes intéressantes pour le suivi des allocations et d'autres problèmes de performance. De plus, si vous avez besoin de détecter une situation de mémoire faible, consultez la page Activity.onLowMemory() .

0 votes

Merci Neil. Cela fonctionne-t-il lorsque l'application est en cours d'exécution et que le débogage est désactivé ?

4 votes

Je suis assez novice en la matière, mais je ne pense pas que le tas natif soit le même que le tas de l'application (Dalvik) auquel la question fait référence. Le tas natif sert de support aux données bitmap, qui sont allouées par le code natif, tandis que le tas app contient les données des applications Java. J'ai été intéressée d'apprendre que le tas natif est compté dans la limite du tas de l'application, et qu'après la version 3.0, les allocations ont lieu sur le tas de l'application. Diane Hackborn (hackbod) publie un article sur ce sujet ici : stackoverflow.com/questions/1945142/bitmaps-en-Android Mais même pour la version 3.0, il y a aussi des données non "natives" sur le tas d'applications.

1 votes

Depuis une mise à jour récente d'Android (peut-être KitKat 4.4), les bitmaps sont dans le tas de la JVM.

6voto

fhucho Points 8964

Ceci renvoie la taille maximale du tas en octets :

Runtime.getRuntime().maxMemory()

J'utilisais ActivityManager.getMemoryClass() mais sur CyanogenMod 7 (je ne l'ai pas testé ailleurs) il renvoie une valeur erronée si l'utilisateur définit manuellement la taille du tas.

1 votes

Puisque le doc pour getMemoryClass semble impliquer que le nombre peut ne pas être le même que la taille de tas disponible pour votre machine virtuelle, et puisque la documentation de getNativeHeapSize est... taciturne, je pense vraiment Runtime.getRuntime().maxMemory() est la meilleure réponse.

1voto

Tkraindesigns Points 31

Asus Nexus 7 (2013) 32Gig : getMemoryClass()=192 maxMemory()=201326592

J'ai fait l'erreur de prototyper mon jeu sur la Nexus 7, puis de découvrir qu'il manquait de mémoire presque immédiatement sur la tablette générique 4.04 de ma femme (memoryclass 48, maxmemory 50331648).

Je vais devoir restructurer mon projet pour charger moins de ressources lorsque je détermine que la classe de mémoire est faible.
(Je peux le voir clairement dans le logCat lors du débogage, mais j'aimerais pouvoir le voir dans le code pour l'adapter, par exemple si currentheap>(maxmemory/2) décharger les bitmaps de haute qualité charger ceux de basse qualité.

0 votes

Sur ledit Nexus7-2013, getLargeMemoryClass() = 512.

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