Détecter la taille du tas d'application dans Android

145

Comment détecter par programme la taille du tas d'application disponible pour une application Android?

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

hpique
la source
2
En relation: stackoverflow.com/questions/1518154/…
Josh Lee

Réponses:

453

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

  1. Combien de tas mon application peut-elle utiliser avant qu'une erreur matérielle ne se déclenche? Et

  2. Quelle quantité de tas mon application doit -elle utiliser, 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 l'élément 1 ci-dessus: maxMemory()

qui peut être invoqué (par exemple, dans la onCreate()méthode de votre activité principale ) 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 d' octets de tas que votre application est autorisée à 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 le nombre de mégaoctets de tas que votre application doit utiliser si elle veut être correctement respectueuse des limites de l'appareil actuel et des droits des autres applications à s'exécuter sans être forcées à plusieurs reprises dans le cycle onStop()/ onResume()car elles sont grossièrement vidé de la mémoire pendant que votre application éléphantine prend un bain dans le jacuzzi 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 propre satisfaction qu'il s'agit d'une interprétation correcte.

Pour une version standard d'Android, maxMemory()retournera généralement environ 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 je suis conscient) pour laquelle les deux méthodes peuvent diverger est sur un appareil enraciné exécutant une version Android telle que CyanogenMod, qui permet à l'utilisateur de sélectionner manuellement la taille du tas qui doit être autorisée pour chaque application. Dans CM, par exemple, cette option apparaît sous "Paramètres CyanogenMod" / "Performance" / "Taille du tas VM".

REMARQUE: SACHEZ QUE RÉGLER CETTE VALEUR MANUELLEMENT PEUT MESSER VOTRE SYSTÈME, SURTOUT si vous sélectionnez une valeur plus petite que la normale pour votre appareil.

Voici les résultats de mes tests montrant les valeurs renvoyées par maxMemory()et getMemoryClass()pour quatre appareils différents exécutant CyanogenMod, en utilisant deux valeurs de tas différentes (définies manuellement) pour chacun:

  • G1:
    • Avec la taille du segment de mémoire VM définie sur 16 Mo:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Avec la taille du segment de mémoire VM définie sur 24 Mo:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Avec la taille du segment de mémoire VM définie sur 24 Mo:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Avec la taille du segment de mémoire VM définie sur 16 Mo:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Avec une taille de segment de mémoire virtuelle définie sur 32 Mo:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Avec une taille de segment de mémoire VM définie sur 24 Mo:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewsonic GTab:
    • Avec VM Heap Size défini sur 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Avec VM Heap Size défini sur 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

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

Pour cet appareil, voici les résultats:

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

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

  • HTC One X
    • maxMemory: 67108864
    • getMemoryClass: 64

Et (d'après le commentaire d'akauppi):

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

Par un commentaire de cmcromance:

  • Grand tas Galaxy S3 (Jelly Bean)
    • maxMemory: 268435456
    • getMemoryClass: 64

Et (d'après les commentaires de Tencent):

  • LG Nexus 5 (4.4.3) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • LG Nexus 5 (4.4.3) gros tas
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) normal
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Grand tas de Galaxy Nexus (4.3)
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Grand tas de Galaxy S4 Play Store Edition (4.4.2)
    • maxMemory: 536870912
    • getMemoryClass: 192

Autres appareils

  • Huawei Nexus 6P (6.0.1) normal
    • maxMemory: 201326592
    • getMemoryClass: 192

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

Mon attente (qui semble être prise en charge par les nombres de largeHeap ci-dessus) serait que cette option aurait un effet similaire à la configuration manuelle du tas via un système d'exploitation 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 à l'aide du 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 rester avec la limite de getMemoryClass ()."

Si j'ai bien deviné, utiliser cette option aurait les mêmes avantages (et risques) que l'utilisation de l'espace rendu disponible par un utilisateur qui a augmenté le tas via un système d'exploitation enraciné (c'est-à-dire, si votre application utilise la mémoire supplémentaire, il ne jouera probablement pas aussi bien avec toutes les autres applications que l'utilisateur exécute en même temps).

Notez que la classe de mémoire n'a apparemment pas besoin d'être un multiple de 8 Mo.

Nous pouvons voir de ce qui précède que le getMemoryClass()résultat est inchangé pour une configuration de périphérique / OS donnée, tandis que la valeur 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 24 Mo comme taille de tas, je peux exécuter sans erreur même si mon utilisation de la mémoire est autorisée à dériver vers 20 Mo (vraisemblablement cela pourrait aller jusqu'à 24 Mo, même si je n'ai pas essayé cela). Mais d'autres applications de même taille peuvent être vidées de la mémoire en raison de la pigmentation de ma propre application. Et, inversement, mon application peut être vidée de la mémoire si ces autres applications nécessitant beaucoup de maintenance sont mises au premier plan par l'utilisateur.

Ainsi, vous ne pouvez pas dépasser la quantité de mémoire spécifiée par maxMemory(). Et vous devriez essayer de rester dans les limites spécifiées par getMemoryClass(). Une façon de faire cela, si tout le reste échoue, pourrait être de limiter les fonctionnalités de ces périphériques de manière à économiser la mémoire.

Enfin, si vous prévoyez de dépasser le nombre de mégaoctets spécifié dans getMemoryClass(), mon conseil serait de travailler longtemps et dur sur la sauvegarde et la restauration de l'état de votre application, afin que l'expérience de l'utilisateur soit pratiquement ininterrompue si un cycle onStop()/ onResume()se produit.

Dans mon cas, pour des raisons de performances, je limite mon application aux appareils exécutant la version 2.2 et supérieure, ce qui signifie que presque tous les appareils exécutant mon application auront une memoryClass de 24 ou plus. Je peux donc concevoir pour occuper jusqu'à 20 Mo de tas et être assez confiant que mon application fonctionnera bien avec les autres applications que l'utilisateur peut exécuter en même temps.

Mais il y aura toujours quelques utilisateurs rooté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, idéalement, vous devriez réduire votre utilisation de la mémoire, même si maxMemory()cela vous dit que vous pouvez aller beaucoup plus haut que les 16 Mo qui getMemoryClass()vous indiquent que vous devriez cibler. 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 de manière transparente.

getMemoryClass(), comme indiqué par Diane Hackborn (hackbod) ci-dessus, n'est disponible qu'au niveau de l'API 5 (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 n'occupant pas plus de 16 Mo d'espace de stockage.

En revanche, maxMemory()selon la documentation, est disponible toute l'arrière jusqu'au niveau de l' API 1. maxMemory(), sur une version pré-2.0, retournera probablement une valeur de 16 Mo, mais je fais voir que dans mes (beaucoup plus tard) versions 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, et je vous suggère donc de continuer à tester la maxMemory()valeur, même pour les versions du système d'exploitation antérieures à 2.0. Vous devrez peut-être même refuser de s'exécuter dans le cas improbable où cette valeur serait même inférieure à 16 Mo, si vous devez avoir plus que ce qui maxMemory()est autorisé.

Carl
la source
2
@Carl Bonjour Carl, Merci pour votre excellent message. Et j'ai testé l'option, android: largeHeap = "true" sur Galaxy S3 avec JellyBean. Voici le résultat. [maxMemory: 256,0 Mo, memoryClass: 64 Mo] (maxMemory était de 64 Mo sans l'option largeheap)
cmcromance
1
@Carl Haha C'est un grand honneur! :)
cmcromance
1
Pour HTC One X: maxMemory: 67108864 memoryClass: 64
Kishore
1
LG Nexus 5 (4.4.3) (normal): maxMemory: 201326592, memoryClass: 192 | LG Nexus 5 (4.4.3) (gros tas): maxMemory: 536870912, memoryClass: 192
ian.shaun.thomas
1
Très bien documenté. Veuillez fournir ceci à Android. Je ne trouve pas un tel document Android approprié
Jimit Patel
20

L' API officielle est:

Cela a été introduit dans la version 2.0 où des périphériques 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 d'origine (16).

hackbod
la source
13

Debug.getNativeHeapSize()fera l'affaire, je devrais penser. Il existe depuis la version 1.0, cependant.

La Debugclasse dispose de nombreuses méthodes intéressantes pour suivre les allocations et autres problèmes de performances. De plus, si vous devez détecter une situation de mémoire insuffisante, vérifiez Activity.onLowMemory().

Neil Traft
la source
Merci Neil. Cela fonctionne-t-il lorsque l'application est en cours d'exécution avec le débogage désactivé?
hpique
2
Je suis assez nouveau dans ce domaine, mais je ne pense pas que le tas natif soit le même que le tas d'application (Dalvik) auquel la question se réfère. Le tas natif fournit une sauvegarde pour les données bitmap, qui sont allouées par le code natif, tandis que le tas d'application contient les données d'application Java. J'ai été intéressé d'apprendre que le tas natif est compté par rapport à la limite du tas d'application, et après 3.0, les allocations se produisent réellement sur le tas de l'application. Diane Hackborn (hackbod) publie des articles sur ce sujet ici: stackoverflow.com/questions/1945142/bitmaps-in-android Mais même pour la version 3.0, il y a aussi des données non «natives» sur le tas d'applications.
Carl
1
Depuis une mise à jour récente d'Android (peut-être KitKat 4.4), les bitmaps sont dans le tas JVM.
akauppi
13

Voici comment procéder:

Obtenir la taille maximale du tas que l'application peut utiliser:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

Obtenir la part du tas que votre application utilise actuellement:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

Obtenir la quantité de tas que votre application peut désormais utiliser (mémoire disponible):

long availableMemory=maxMemory-usedMemory;

Et, pour bien formater chacun d'eux, vous pouvez utiliser:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 
développeur android
la source
5

Cela 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 incorrecte si l'utilisateur définit la taille du tas manuellement.

fhucho
la source
Puisque la documentation pour getMemoryClasssemble impliquer que le nombre peut ne pas être le même que la taille de tas disponible pour votre vm, et puisque la documentation pour getNativeHeapSizeest ... taciturne, je pense vraiment que Runtime.getRuntime().maxMemory()c'est la meilleure réponse.
BoD
3

Certaines opérations sont plus rapides que le gestionnaire d'espace de tas Java. Retarder les opérations pendant un certain temps peut libérer de l'espace mémoire. Vous pouvez utiliser cette méthode pour éviter l'erreur de taille de tas:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * space.
 *
 * @param callback Callback operations to be done when memory is free.
 */
public static void waitForGarbageCollector(final Runnable callback) {

  Runtime runtime;
  long maxMemory;
  long usedMemory;
  double availableMemoryPercentage = 1.0;
  final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
  final int DELAY_TIME = 5 * 1000;

  runtime =
    Runtime.getRuntime();

  maxMemory =
    runtime.maxMemory();

  usedMemory =
    runtime.totalMemory() -
    runtime.freeMemory();

  availableMemoryPercentage =
    1 -
    (double) usedMemory /
    maxMemory;

  if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {

    try {
      Thread.sleep(DELAY_TIME);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    waitForGarbageCollector(
      callback);
  } else {

    // Memory resources are availavle, go to next operation:

    callback.run();
  }
}
Zon
la source
testé Bon. Pouvez-vous expliquer ces deux éléments: availableMemoryPercentage et MIN_AVAILABLE_MEMORY_PERCENTAGE? quelques explications?
Noor Hossain
1
AvailableMemoryPercentageest selon la formule: combien de mémoire de l'appareil est actuellement libre. MIN_AVAILABLE_MEMORY_PERCENTAGEest votre paramètre personnalisé, un seuil à partir duquel vous commencez à attendre que le garbage collector fasse son travail.
Zon
1

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

J'ai fait l'erreur de prototyper mon jeu sur le 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)

J'aurai besoin de restructurer mon projet pour charger moins de ressources lorsque je déterminerai que la classe de mémoire est faible.
Existe-t-il un moyen en Java de voir la taille actuelle du tas? (Je peux le voir clairement dans le logCat lors du débogage, mais j'aimerais un moyen de le voir dans le code pour s'adapter, comme si currentheap> (maxmemory / 2) déchargez des bitmaps de haute qualité chargent de faible qualité

Tkraindesigns
la source
Sur ledit Nexus7-2013, getLargeMemoryClass () = 512.
akauppi
0

Voulez-vous dire par programme ou juste pendant le développement et le débogage? Dans ce dernier cas, vous pouvez voir ces informations du point de vue DDMS dans Eclipse. Lorsque votre émulateur (peut-être même un téléphone physique branché) est en cours d'exécution, il répertorie les processus actifs dans une fenêtre sur la gauche. Vous pouvez le sélectionner et il existe une option pour suivre les allocations de tas.

Steve Haley
la source
Je veux dire par programme. Clarifié la question. Merci.
hpique
1
Je vous suggère de regarder ce post par l'un des programmeurs de la plate-forme Android alors: stackoverflow.com/questions/2298208/…
Steve Haley
0
Runtime rt = Runtime.getRuntime();
rt.maxMemory()

la valeur est b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

la valeur est Mo

qinqie
la source
Quel type de rt? Où est-il déclaré?
FindOut_Quran