true studio outofmemoryerror outofmemory largeheap images bitmaps android out-of-memory

studio - outofmemory android bitmap



BitmapFactory OOM me vuelve loco (5)

He estado buscando mucho y sé que muchas otras personas están experimentando los mismos problemas de memoria OOM con BitmapFactory . Mi aplicación solo muestra una memoria total disponible de 4MB usando Runtime.getRuntime ().totalMemory() . Si el límite es de 16 MB, ¿por qué no crece la memoria total para dejar espacio para el mapa de bits? En su lugar, arroja un error.

Tampoco entiendo que si tengo 1.6 MB de memoria libre de acuerdo con Runtime.getRuntime().freeMemory() ¿por qué recibo un error que dice "VM no nos permitirá asignar 614400 bytes"? Me parece que tengo mucha memoria disponible.

Mi aplicación está completa, excepto por este problema, que desaparece cuando reinicio el teléfono para que mi aplicación sea la única en ejecución. Estoy usando un HTC Hero para probar dispositivos (Android 1.5).

En este punto, creo que la única forma de evitar esto es evitar de alguna manera el uso de BitmapFactory .

¿Alguien tiene alguna idea sobre esto o una explicación de por qué VM no asignará 614 KB cuando hay 1.6 MB de memoria libre?


1.6 MB de memoria parece mucho, pero podría ser el caso de que la memoria esté tan fragmentada que no pueda asignar un gran bloque de memoria de una vez (aún suena muy extraño).

Una de las causas comunes de OOM durante el uso de recursos de imágenes es cuando se descomprimen las imágenes JPG, PNG, GIF con resoluciones realmente altas. Debe tener en cuenta que todos estos formatos están bastante bien comprimidos y ocupan muy poco espacio, pero una vez que carga las imágenes en el teléfono, la memoria que van a utilizar es algo así como width * height * 4 bytes . Además, cuando se inicia la descompresión, es necesario cargar algunas otras estructuras de datos auxiliares para el paso de decodificación.


Aunque esta es una respuesta de alto nivel, el problema para mí resultó ser la aceleración de hardware en todos mis puntos de vista. La mayoría de mis vistas tienen una manipulación de mapa de bits personalizada, que calculé que era la fuente del gran tamaño de heap nativo, pero de hecho al deshabilitar la aceleración de hardware el uso del heap nativo se redujo por un factor de 4.

Parece que la aceleración de hardware hará todo tipo de almacenamiento en caché en sus vistas, creando mapas de bits propios, y dado que todos los mapas de bits comparten el montón nativo, el tamaño de la asignación puede crecer bastante dramáticamente.


Aunque normalmente no tiene sentido detectar un error porque generalmente son lanzados solo por el vm pero en este caso particular, el error es arrojado por el código jni glue por lo que es muy simple manejar casos donde no se puede cargar la imagen: solo atrapa el OutOfMemoryError.


Parece que los problemas que figuran en la respuesta de Torid se han resuelto en las versiones más recientes de Android.

Sin embargo, si está utilizando un caché de imágenes (uno especializado o incluso un HashMap normal), es muy fácil obtener este error creando una pérdida de memoria.

En mi experiencia, si inadvertidamente se aferra a sus referencias de Bitmap y crea una fuga de memoria, el error de OP (que hace referencia a BitmapFactory y métodos nativos) es el que bloqueará su aplicación (hasta ICS - 14 y +?)

Para evitar esto, haga que "suelte" sus Bitmaps. Esto significa usar SoftReferences en el nivel final de su caché, de modo que Bitmaps pueda obtener la recolección de basura. Esto debería funcionar, pero si sigues teniendo bloqueos, puedes intentar marcar explícitamente ciertos mapas de bits para la recopilación usando bitmap.recycle() , solo recuerda nunca devolver un mapa de bits para usar en tu aplicación si bitmap.isRecycled() .

Por otro lado, LinkedHashMaps es una gran herramienta para implementar fácilmente estructuras de caché bastante buenas, especialmente si combina referencias duras y suaves como en este ejemplo (línea de inicio 308) ... pero el uso de referencias duras también es la forma de ingresar en la memoria situaciones de fuga si te equivocas.


[Tenga en cuenta que (como dice CommonsWare a continuación), el enfoque completo en esta respuesta solo aplica hasta 2.3.x (Gingerbread). A partir de Honeycomb, los datos de mapa de bits se asignan en el montón de VM.]

Los datos de mapa de bits no están asignados en el montón de VM. Hay una referencia en el montón de VM (que es pequeño), pero la biblioteca subyacente de Skia asigna los datos reales en el montón nativo.

Desafortunadamente, mientras que la definición de BitmapFactory.decode ... () dice que devuelve null si los datos de la imagen no se pueden decodificar, la implementación de Skia (o más bien el pegamento JNI entre el código de Java y Skia) registra el mensaje que está seeing ("VM no nos permite asignar xxxx bytes") y luego lanza una excepción OutOfMemory con el mensaje engañoso "el tamaño del mapa de bits excede el presupuesto de VM".

El problema no está en el montón de VM, sino en el montón nativo. El montón de Native se comparte entre las aplicaciones en ejecución, por lo que la cantidad de espacio libre depende de qué otras aplicaciones se estén ejecutando y del uso de su mapa de bits. Pero, dado que BitmapFactory no volverá, necesita una forma de averiguar si la llamada tendrá éxito antes de hacerlo.

Existen rutinas para monitorear el tamaño del montón nativo (vea los métodos getNative de la clase Debug). Sin embargo, he encontrado que getNativeHeapFreeSize () y getNativeHeapSize () no son confiables. Entonces, en una de mis aplicaciones que crea dinámicamente una gran cantidad de bitmaps, hago lo siguiente.

El tamaño del montón nativo varía según la plataforma. Por lo tanto, al inicio, verificamos el tamaño máximo de almacenamiento dinámico de máquinas virtuales permitido para determinar el tamaño de almacenamiento dinámico nativo máximo permitido. [Los números mágicos se determinaron probando en 2.1 y 2.2, y pueden ser diferentes en otros niveles de API.]

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);

Luego, cada vez que necesitamos llamar a BitmapFactory, precedemos a la llamada marcando el formulario.

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

Tenga en cuenta que heapPad es un número mágico que permite el hecho de que a) el informe del tamaño del montón nativo es "blando" yb) queremos dejar algo de espacio en el montón nativo para otras aplicaciones. Estamos corriendo con un pad 3 * 1024 * 1024 (es decir, 3Mbytes) actualmente.