web-dev-qa-db-fra.com

Android: OutofMemoryError: la taille des images dépasse VM budget sans raison je peux voir

J'ai une exception OutOfMemory avec une galerie de plus de 600x800 pixels JPEG.


L'environnement

J'utilise Gallery avec des images JPG d'environ 600x800 pixels.

Étant donné que mon contenu peut être un peu plus complexe que de simples images, j'ai défini chaque vue comme étant un RelativeLayout qui englobe ImageView avec le fichier JPG.

Afin "d'accélérer" l'expérience utilisateur, j'ai un cache simple de 4 emplacements qui pré-prélève (dans un boucleur) environ 1 image à gauche et 1 image à droite de l'image affichée et les conserve dans un HashMap 4 emplacements. 

La plateforme

J'utilise AVD de 256 RAM et 128 Heap Size, avec un écran de 600 x 800 . Cela se produit également sur une cible Entourage Edge, sauf qu'avec le périphérique, il est plus difficile de déboguer.


Le problème

J'ai eu une exception:

OutofMemoryError: bitmap size exceeds VM budget

Et cela se produit lors de la récupération de la cinquième image. J'ai essayé de changer la taille de mon cache d'image, et c'est toujours la même chose.


La chose étrange: il ne devrait pas y avoir de problème de mémoire

Afin de m'assurer que la limite de tas est très éloignée de ce dont j'ai besoin, j'ai défini un tableau factice de 8 Mo au début, et je ne l'ai pas référencé pour qu'il soit immédiatement envoyé. C'est un membre du fil d'activité et est défini comme suit

static { @SuppressWarnings("unused")
byte dummy[] = new byte[ 8*1024*1024 ]; }    

Le résultat est que la taille du segment de mémoire est proche de 11 Mo et que tout est gratuit .Remarque J'ai ajouté cette astuce après le début de son crash. Cela rend OutOfMemory moins fréquent.

Maintenant, j'utilise DDMS. Juste avant le crash (ne change pas beaucoup après le crash), DDMS indique:

ID  Heap Size   Allocated   Free       %Used    #Objects
1   11.195 MB   2.428 MB    8.767 MB   21.69%   47,156  

Et dans le tableau de détail, il montre:

Type  Count  Total Size   Smallest   Largest   Median    Average
free  1,536  8.739MB      16B        7.750MB   24B       5.825KB

Le bloc le plus grand mesure 7,7 Mo. Et pourtant, le LogCat dit:

ERROR/dalvikvm-heap(1923): 925200-byte external allocation too large for this process.

Si vous tenez compte de la relation entre la médiane et la moyenne, il est plausible de supposer que la plupart des blocs disponibles sont très petits. Cependant, il y a un bloc assez grand pour le bitmap, il est 7.7M. Comment se fait-il que cela ne soit toujours pas suffisant?

Remarque: j'ai enregistré une trace de tas. Lorsqu'on examine la quantité de données allouées, on ne sent pas que plus de 2 millions de dollars sont alloués. Cela correspond au rapport de mémoire disponible de DDMS.


  • Est-ce que je pourrais avoir un problème comme la fragmentation de tas?
  • Comment résoudre/contourner le problème?
  • Le tas est-il partagé avec tous les threads?
  • Est-il possible que j'interprète mal la lecture DDMS et qu'il n'y ait pas réellement de bloc 900K à allouer? Si oui, quelqu'un peut-il me dire où je peux voir cela?

Merci beaucoup

Meymann

34
Meymann

Je pense qu'il n'y a rien de spécial dans votre cas. Il n'y a tout simplement pas assez de mémoire. Vous ne pouvez pas avoir plusieurs bitmaps 600x800 en mémoire, ils consomment trop de mémoire. Vous devez les enregistrer sur SD et les charger en mémoire à la demande. Je pense que c'est exactement ce que vous faites.

Vous devez savoir une chose: DDMS affiche la consommation de mémoire Java. Mais il y a aussi de la mémoire native qui n'est pas affichée dans DDMS. Et les images, pour autant que je sache, sont créées dans la mémoire native. DDMS est donc un mauvais outil pour suivre ces problèmes de mémoire. Vous devez simplement vous assurer que vous libérez de la mémoire, que les images sont collectées par Garbage Collector une fois que vous n'en avez plus besoin.

Garbage Collector fonctionne selon son propre calendrier. C'est pourquoi vous devriez appeler la méthode Bitmap.recycle () sur des bitmaps dont vous n'avez plus besoin. Cette méthode libère exactement la mémoire native qui vous manque. De cette façon, vous ne dépendez pas de GC et vous pourrez libérer le plus gros morceau de mémoire dès que possible.

Tout d’abord, vous devez vous assurer que vous ne perdez pas de bitmaps.

Voici un Nice post sur les allocations de mémoire, cela peut vous aider à creuser plus profondément 

12
Fedor

Vous ne savez pas si c'est une option pour vous, mais avez-vous essayé le suréchantillonnage d'images Étrange problème de mémoire insuffisante lors du chargement d'une image dans un objet Bitmap ?

5
Fedor

La question a été posée en 2010, lorsque Froyo était frais. Tant de choses se sont passées depuis… .. Avant la version 3.0, les bitmaps étaient alloués dans JNI. La mémoire ne figurait pas dans les statistiques de Dalvik. Cela ne doit plus être monolithique . Avant la version 2.3, les statistiques de mémoire pour JNI n'étaient pas disponibles (appels de décodage bitmap JNI) dans logcat . En 2010, Nexus One était haut de gamme, avec moins de 300 Mo. Le budget pour une application était d'environ 16MB. De nos jours, il y a environ 8 fois cette mémoire.

0
Meymann

J'ai également été confronté à un problème similaire il y a quelques semaines et je l'ai résolu en réduisant les images jusqu'à un point optimal. J'ai écrit l'approche complète dans mon blog ici et j'ai téléchargé un exemple de projet complet avec un code sujet à MOO contre un code de validation MOO ici .

Il y a eu beaucoup de temps depuis que j'ai posé cette question.

La réponse devrait être divisée en deux parties: Essayez de vous assurer que vous n'allouez pas de petits éléments que vous ne pouvez pas libérer avant d'obtenir un bitmap. Avant Ginger, la mémoire pour bmps devait être continue, et elle n’était pas comptée dans la mémoire VM. Toujours regarder le logcat. Voir une conférence sur la mémoire de Google IO 2011 . Publier Ginger c'est plus facile. Depuis Honeycomb, les bitmaps sont même comptabilisés dans votre zone Java. Il n'y a pas de zone JNI… .. Utilisez toujours recycler pour les bitmaps dont vous n'avez pas besoin. N'attendez pas le GC.

0
Meymann