Quelle est la quantité maximale de RAM qu'une application peut utiliser?

145

Je suis assez curieux de connaître cette question concernant la gestion de la mémoire du système d'exploitation Android et j'espère donc une réponse assez détaillée à ce sujet.

Ce que je voudrais savoir:

  • Quelle est la quantité maximale de mémoire (en mégaoctets / en pourcentage de la RAM totale) qu'une application Android (qui n'est pas une application système) peut utiliser?
  • Y a-t-il des différences entre les versions d'Android ?
  • Y a-t-il des différences concernant le fabricant de l'appareil?

Et, surtout:

  • Qu'est - ce qui est considéré / de quoi dépend -il quand il s'agit de déterminer la quantité de RAM qu'une application peut utiliser au moment de l'exécution (en supposant que la mémoire maximale par application n'est pas un nombre statique)?

Ce que j'ai entendu jusqu'à présent (jusqu'en 2013):

  • Les premiers appareils Android avaient une limite par application de 16 Mo
  • Plus tard, ce plafond est passé à 24 Mo ou 32 Mo

Ce qui me rend très curieux:

Ces deux limites sont très faibles.

Je viens de télécharger le gestionnaire de tâches Android pour vérifier la RAM de mes appareils. Ce que j'ai remarqué, c'est qu'il existe des applications utilisant environ 40 à 50 mégaoctets de RAM, ce qui est évidemment plus que l'utilisation maximale de RAM mentionnée, disons 32 Mo. Alors, comment Android détermine-t-il la quantité de RAM qu'une application peut utiliser? Comment est-il possible que les applications dépassent cette limite?

De plus, j'ai remarqué que certaines de mes applications se bloquent (tuées par le système?) Avec une OutOfMemoryException lors de l'utilisation d'environ 30 à 40 mégaoctets. D'un autre côté, j'ai des applications exécutées sur mon téléphone en utilisant 100 Mo et plus après un certain temps (probablement en raison de fuites de mémoire) qui ne se bloquent pas ou ne sont pas tuées. Donc, cela dépend évidemment aussi de l'application elle - même lorsqu'il s'agit de déterminer la quantité de RAM pouvant être épargnée. Comment est-ce possible? (J'ai effectué mes tests avec un HTC One S avec 768 Mo de RAM)

Clause de non-responsabilité: je ne suis en aucun cas affilié à l'application Android Task Manager.

Philipp Jahoda
la source

Réponses:

119

Quelle est la quantité maximale de mémoire (en mégaoctets / en pourcentage de la RAM totale) qu'une application Android (qui n'est pas une application système) peut utiliser?

Cela varie selon l'appareil. getMemoryClass()onActivityManager vous donnera la valeur de l'appareil sur lequel votre code s'exécute.

Y a-t-il des différences entre les versions d'Android?

Oui, dans la mesure où les exigences du système d'exploitation ont augmenté au fil des ans, et les appareils doivent s'adapter pour correspondre.

Y a-t-il des différences concernant le fabricant de l'appareil?

Oui, dans la mesure où les fabricants fabriquent des appareils, et la taille varie selon l'appareil.

Quels «facteurs secondaires» sont pris en compte lorsqu'il s'agit de déterminer la quantité de RAM qu'une application peut utiliser?

Je n'ai aucune idée de ce que signifie «facteurs secondaires».

Les premiers appareils avaient une limite par application de 16 Mo; Les appareils plus récents ont augmenté cela à 24 Mo ou 32 Mo

C'est à peu près juste. La résolution de l'écran est un déterminant important, car des résolutions plus élevées signifient des bitmaps plus grands, et donc les tablettes et les téléphones haute résolution auront tendance à avoir encore des valeurs plus élevées. Par exemple, vous verrez des appareils avec des tas de 48 Mo, et je ne serais pas surpris qu'il y ait des valeurs supérieures à cela.

Comment est-il possible que les applications dépassent cette limite?

Vous supposez que l'auteur de cette application sait ce qu'il fait. Étant donné que l' utilisation de la mémoire d'une application est difficile à déterminer pour un ingénieur Android principal , je ne suppose pas que l'application en question fournit nécessairement des résultats particulièrement précis.

Cela étant dit, le code natif (NDK) n'est pas soumis à la limite du tas. Et, depuis Android 3.0, les applications peuvent demander un "gros tas", généralement de l'ordre de centaines de Mo, mais c'est considéré comme une forme médiocre pour la plupart des applications.

De plus, j'ai remarqué que certaines de mes applications se bloquent avec une exception OutOfMemoryException lors de l'utilisation d'environ 30 à 40 mégaoctets.

Gardez à l'esprit que le garbage collector Android n'est pas un garbage collector compact. L'exception devrait vraiment être CouldNotFindSufficientlyLargeBlockOfMemoryException, mais cela a probablement été jugé trop verbeux. OutOfMemoryExceptionsignifie que vous ne pouvez pas allouer votre bloc demandé , pas que vous avez entièrement épuisé votre tas.

CommonsWare
la source
Je ne peux pas comprendre la table, j'ai Xperia X mobile qui a une résolution d'environ 1080 x 1920 qui est une grande résolution, et un autre appareil Samsung Tab 4 cette résolution est de 800 x 1280, donc il prend la même occupation de RAM, veuillez me guider car le mobile est livré avec 3 Go de RAM et l'onglet est livré avec 1,5 Go de RAM, donc la tablette occupe une grande RAM en raison du grand écran?
Rahul Mandaliya
@RahulMandaliya: Je suis désolé, mais je ne comprends pas votre inquiétude ni ce que cela a à voir avec cette question. Vous souhaiterez peut-être ouvrir une autre question Stack Overflow dans laquelle vous expliquez en détail votre préoccupation.
CommonsWare
15

C'est la fin de 2018 donc les choses ont changé.

Tout d'abord: exécutez votre application et ouvrez l'onglet Android Profiler dans Android Studio. Vous verrez combien de mémoire il consomme, vous serez surpris mais il peut allouer beaucoup de RAM.

Voici également un excellent article dans la documentation officielle avec des instructions détaillées sur l'utilisation de Memory Profiler qui peuvent vous donner un aperçu détaillé de la gestion de votre mémoire.

Mais dans la plupart des cas, votre profileur Android régulier vous suffira.

entrez la description de l'image ici

Habituellement, une application commence avec 50 Mo d'allocation de RAM, mais passe instantanément à 90 Mo lorsque vous commencez à charger des photos en mémoire. Lorsque vous ouvrez Activity avec un ViewPager avec des photos préchargées (3,5 Mo chacune), vous pouvez facilement obtenir 190 Mo en quelques secondes.

Mais cela ne signifie pas que vous avez des problèmes avec la gestion de la mémoire.

Le meilleur conseil que je puisse donner est de suivre les directives et les meilleures pratiques, d'utiliser les meilleures bibliothèques pour le chargement d'images (Glide, Picasso) et tout ira bien.


Mais si vous avez besoin de personnaliser quelque chose et que vous avez vraiment besoin de savoir combien de mémoire vous pouvez allouer manuellement, vous pouvez obtenir la mémoire totale disponible et en calculer une partie prédéterminée (en%). Dans mon cas, j'avais besoin de mettre en cache les photos déchiffrées en mémoire afin de ne pas avoir besoin de les déchiffrer à chaque fois que l'utilisateur glisse dans la liste.

Pour cela, vous pouvez utiliser la classe LruCache prête à l'emploi . C'est une classe de cache qui suit automatiquement la quantité de mémoire allouée par vos objets (ou le nombre d'instances) et supprime la plus ancienne pour conserver la plus récente par leur historique d'utilisation. Voici un excellent tutoriel sur la façon de l'utiliser.

Dans mon cas, j'ai créé 2 instances de caches: pour les pouces et les pièces jointes. Rendez-les statiques avec un accès unique afin qu'ils soient disponibles dans le monde entier dans toute l'application.

classe de cache:

public class BitmapLruCache extends LruCache<Uri, byte[]> {

    private static final float CACHE_PART_FOR_THUMBS_PRC = 0.01f; // 1% (Nexus 5X - 5Mb)
    private static final float CACHE_PART_FOR_ATTACHMENTS_PRC = 0.03f;// 3% (Nexus 5X - 16Mb)
    private static BitmapLruCache thumbCacheInstance;
    private static BitmapLruCache attachmentCacheInstance;

public static synchronized BitmapLruCache getDecryptedThumbCacheInstance() {
    if (thumbCacheInstance == null) {

        int cacheSize = getCacheSize(CACHE_PART_FOR_THUMBS_PRC);
    //L.log("creating BitmapLruCache for Thumb with size: " + cacheSize + " bytes");
        thumbCacheInstance = new BitmapLruCache(cacheSize);
        return thumbCacheInstance;
    } else {
        return thumbCacheInstance;
    }
}

public static synchronized BitmapLruCache getDecryptedAttachmentCacheInstance() {
    if (attachmentCacheInstance == null) {

        int cacheSize = getCacheSize(CACHE_PART_FOR_ATTACHMENTS_PRC);
    //            L.log("creating BitmapLruCache for Attachment with size: " + cacheSize + " bytes");
        attachmentCacheInstance = new BitmapLruCache(cacheSize);
        return attachmentCacheInstance;
    } else {
        return attachmentCacheInstance;
    }
}

private BitmapLruCache(int maxSize) {
    super(maxSize);
}

public void addBitmap(Uri uri, byte[] bitmapBytes) {
    if (get(uri) == null && bitmapBytes != null)
        put(uri, bitmapBytes);
}

public byte[] getBitmap(Uri uri) {
    return get(uri);
}


@Override
protected int sizeOf(Uri uri, byte[] bitmapBytes) {
    // The cache size will be measured in bytes rather than number of items.
    return bitmapBytes.length;
}
}

Voici comment je calcule la RAM libre disponible et combien je peux en tirer:

private static int getCacheSize(float partOfTotalFreeMemoryToUseAsCache){
    final long maxMemory = Runtime.getRuntime().maxMemory();
    //Use ... of available memory for List Notes thumb cache
    return (int) (maxMemory * partOfTotalFreeMemoryToUseAsCache);
}

Et voici comment je l'utilise dans Adapters pour obtenir une image en cache:

byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);

et comment je l'ai mis en cache dans le thread d'arrière-plan (AsyncTask normal):

BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes); 

Mon application cible l'API 19+, donc les appareils ne sont pas vieux et ces portions de RAM disponible sont assez bonnes pour le cache dans mon cas (1% et 3%).

Fait amusant: Android ne possède pas d'API ou d'autres hacks pour obtenir la quantité de mémoire allouée à votre application, elle est calculée à la volée en fonction de divers facteurs.


PS J'utilise un champ de classe statique pour contenir un cache, mais selon les dernières directives Android, il est recommandé d'utiliser le composant d'architecture ViewModel à cette fin.

Kirill Karmazin
la source