31 votes

BitmapFactory OOM me rend fou

J'ai fait beaucoup de recherche et je sais que beaucoup d'autres personnes rencontrez le même OOM des problèmes de mémoire avec BitmapFactory. Mon l'app montre seulement une mémoire totale disponible de 4 MO à l'aide de Runtime.getRuntime ().totalMemory(). Si la limite est de 16 mo, alors pourquoi ne pas le total mémoire de pousser pour faire de la place pour l'image? Au lieu de cela, il renvoie une erreur.

Aussi, je ne comprends pas que si j'ai 1.6 MO de mémoire libre selon d' Runtime.getRuntime().freeMemory() pourquoi j'obtiens un message d'erreur indiquant "VM ne nous laisse pas allouer 614400 octets"? Me semble que j'ai beaucoup la mémoire disponible.

Mon application est complète, sauf pour ce problème, qui s'en va quand j' redémarrer le téléphone pour que mon application est la seule chose en cours d'exécution. Je suis à l'aide d' un HTC Hero pour les essais de l'instrument (Android 1.5).

À ce point, je pense, la seule façon de contourner cela est d'une certaine façon évitez d'utiliser des BitmapFactory.

N'importe qui ont des idées sur ce produit ou d'une explication quant à pourquoi les VM ne sera pas allouer 614KB quand il y a 1.6 MO de mémoire libre?

47voto

Torid Points 2821

[Remarque que (comme CommonsWare points ci-après), l'ensemble de la démarche dans cette réponse ne s'applique que jusqu'à et y compris 2.3.x (Gingerbread). Comme de Nid d'abeille données Bitmap est allouée à la machine virtuelle tas.]

Données de l'image n'est pas allouée à la machine virtuelle tas. Il est une référence dans la VM tas (qui est petite), mais les données réelles est alloué dans le tas Natif par le sous-jacent Skia bibliothèque graphique.

Malheureusement, bien que la définition de BitmapFactory.décoder...() dit qu'elle retourne null si les données d'image ne peut pas être décodé, le Skia de mise en œuvre (ou plutôt la JNI de la colle entre le code Java et Skia) enregistre le message que vous avez vu ("VM ne nous laisse pas allouer xxxx octets") et puis lève une exception OutOfMemory avec le message trompeur "bitmap taille dépasse VM budget".

Le problème n'est pas dans la machine virtuelle tas, mais est plutôt dans le tas Natif. Le Natïve segment est partagé entre les applications en cours d'exécution, et donc la quantité d'espace libre dépend de ce que d'autres applications sont en cours d'exécution et leur image bitmap d'utilisation. Mais, étant donné que BitmapFactory ne sera pas de retour, vous avez besoin d'une manière de déterminer si l'appel est de réussir avant de vous rendre.

Il y a des routines pour surveiller la taille du tas Natif (voir la classe Debug getNative méthodes). Cependant, j'ai trouvé que getNativeHeapFreeSize() et getNativeHeapSize() ne sont pas fiables. Dans une de mes applications qui crée dynamiquement un grand nombre d'images je ne les suivants.

Le Natif de la taille du segment varie selon la plate-forme. Donc, au démarrage, nous vérifions le maximum permis par la VM taille de segment de mémoire pour déterminer le maximum autorisé Natif de la taille du segment. [La magie des nombres ont été déterminées par des essais sur 2.1 et 2.2, et peut être différent sur d'autres API niveaux.]

long mMaxVmHeap     = Runtime.getRuntime().maxMemory()/1024;
long mMaxNativeHeap = 16*1024;
if (mMaxVmHeap == 16*1024)
     mMaxNativeHeap = 16*1024;
else if (mMaxVmHeap == 24*1024)
     mMaxNativeHeap = 24*1024;
else
    Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap);

Ensuite, à chaque fois, nous devons appeler BitmapFactory nous faire précéder l'appel par un contrôle de la forme.

long sizeReqd        = bitmapWidth * bitmapHeight * targetBpp  / 8;
long allocNativeHeap = Debug.getNativeHeapAllocatedSize();
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap)
{
    // Do not call BitmapFactory…
}

Notez que le heapPad est un nombre magique pour permettre le fait que a) la déclaration des Indigènes de la taille du segment est "doux" et b) nous voulons laisser de l'espace dans le tas Natif pour d'autres applications. Nous sommes en cours d'exécution avec un 3*1024*1024 (c'est à dire 3Mbytes) pad actuellement.

2voto

rui Points 4282

1.6 MO de mémoire semble comme beaucoup, mais ce pourrait être le cas que la mémoire est tellement fragmentés qu'il ne peut pas allouer de tels gros bloc de mémoire en une seule fois (encore ce ne sembler très étrange).

Une cause commune de OOM lors de l'utilisation de l'image des ressources est quand on est de la décompression, JPG, PNG, GIF pour les images à très haute résolution. Vous devez garder à l'esprit que tous ces formats sont assez bien compressé et prennent très peu de place, mais une fois que vous chargez les images sur le téléphone, la mémoire qu'ils vont utiliser est quelque chose comme width * height * 4 bytes. Aussi, lors de la décompression des coups de pied dans, quelques autres auxiliaires structures de données doivent être chargés pour le décodage de l'étape.

2voto

Peter Ajtai Points 26377

Il semble que les problèmes qui en Torid de réponse ont été résolus dans les versions plus récentes d'Android.

Toutefois, si vous utilisez une image de cache (spécialisée ou même juste un HashMap), il est assez facile d'obtenir cette erreur par la création d'une fuite de mémoire.

Dans mon expérience, si vous avez, par inadvertance, accrochez-vous à votre Bitmap références et créer une fuite de mémoire, OP erreur (se référant à la BitmapFactory et des méthodes indigènes) est celui qui va planter votre application (jusqu'à ICS - 14 et +?)

Pour éviter cela, faire de votre vous "laisser aller" de vos Images. Cela signifie en utilisant SoftReferences dans la couche finale de votre cache, de sorte que les images peuvent obtenir des ordures collectées hors de lui. Cela devrait fonctionner, mais si vous êtes encore en train tombe en panne, vous pouvez essayer de marquer explicitement certaines images de la collection en utilisant bitmap.recycle(), rappelez-vous juste de ne jamais revenir un bitmap pour une utilisation dans votre application si bitmap.isRecycled().

En aparté, LinkedHashMaps sont un excellent outil pour faciliter la mise en œuvre assez bon cache structures, surtout si vous combinez durs et mous de références comme dans cet exemple (à partir de la ligne 308)... mais dur à l'aide de références est aussi la façon dont vous pouvez obtenir vous-même dans la mémoire de la fuite de ces situations, si vous vous trompez.

0voto

Zsolt Safrany Points 1688

Bien que cela n'ait généralement pas de sens d'attraper une erreur, car ils ne sont généralement générés que par le vm, mais dans ce cas particulier, l'erreur est générée par le code de colle jni, il est donc très simple de gérer les cas où vous ne pouviez pas charger l'image: juste attraper OutOfMemoryError.

0voto

minism Points 677

Même si c'est un niveau assez élevé de réponse, le problème pour moi s'est avéré être en utilisant l'accélération matérielle sur l'ensemble de mon point de vue. La plupart de mes points de vue ont personnalisé Bitmap manipulation, qui je pensais être la source de la grande natif de la taille du segment, mais en fait, lors de la désactivation de l'accélération matérielle le tas natif d'utilisation a été abattu par un facteur de 4.

Il semble que l'accélération matérielle pour faire toutes sortes de mise en cache sur votre point de vue, la création de ses propres bitmaps, et depuis toutes les bitmaps partager le tas natif, la taille de l'allocation peut pousser assez spectaculaire.

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