2010-06-14 22 views
34

Tengo una excepción OutOfMemory con una galería de más de 600x800 píxeles JPEG.Android: OutofMemoryError: el tamaño del mapa de bits excede el presupuesto de VM sin ningún motivo. Puedo ver


El entorno

He estado usando la galería con las imágenes JPG alrededor de 600x800 píxeles.

Dado que mi contenido puede ser un poco más complejo que las imágenes, he configurado cada vista como una RelativeLayout que envuelve ImageView con el JPG.

Para "acelerar" la experiencia del usuario Tengo un caché simple de 4 ranuras que precaptura (en un looper) aproximadamente 1 imagen a la izquierda y 1 imagen a la imagen mostrada y las mantiene en un mapa HashMap de 4.

La plataforma

estoy usando AVD de 256 RAM y 128 Montón tamaño, con una pantalla de 600x800. También ocurre en un destino Entourage Edge, excepto que con el dispositivo es más difícil de depurar.


El problema

He estado recibiendo una excepción:

OutofMemoryError: bitmap size exceeds VM budget 

Y sucede cuando ir a buscar la quinta imagen. Intenté cambiar el tamaño de la memoria caché de mi imagen, y sigue siendo la misma.


Lo extraño: No debería ser un problema de memoria

Con el fin de asegurarse de que el límite de montón es muy lejos de lo que necesito, he definido un conjunto de 8 MB maniquí en el comenzando, y lo dejó sin referencia, por lo que se envía de inmediato. Es un miembro de la rosca actividad y se define como siguiente

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

El resultado es que el tamaño del montón es casi 11MB y todo es gratis. Nota He agregado ese truco después de que comenzó a fallar. Hace que OutOfMemory sea menos frecuente.

Ahora, estoy usando DDMS. Justo antes del accidente (no cambia mucho después del accidente), DDMS muestra:

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

Y en la tabla de detalle que muestra:

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

El bloque más grande es 7.7MB. Sin embargo, el LogCat dice:

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

Si te importa la relación entre la mediana y la media, es plausible suponer que la mayoría de los bloques disponibles son muy pequeñas. Sin embargo, hay un bloque lo suficientemente grande para el mapa de bits, es 7.7M. ¿Cómo es que todavía no es suficiente?

Nota: Grabé una línea de pila. Al observar la cantidad de datos asignados, no parece que se hayan asignado más de 2M. Coincide con el informe de memoria libre por DDMS.


  • Podría ser que lo experimentan algún problema como el de lixiviación en la fragmentación?
  • ¿Cómo resuelvo/soluciono el problema?
  • ¿El montón se comparte con todos los hilos?
  • ¿Podría ser que interprete la lectura DDMS de una manera incorrecta, y realmente no hay un bloque 900K para asignar? Si es así, ¿alguien puede decirme dónde puedo ver eso?

Muchas gracias

Meymann

+0

Puede obtener algunas picaduras aquí si publica el código que realmente está buscando/decodificando/almacenando en caché/expirando los mapas de bits. El problema es casi seguro que es lo que está haciendo allí, no algo que requiera profundizar en el funcionamiento interno de la asignación del montón. –

+0

Al depurar código, mi principal sospechoso es siempre mi código. Para depurarlo, obtengo sugerencias del entorno. Desafortunadamente, en este caso: A. crear un código simple que lea 3 imágenes de 600x800 a la vez en un caché arrojaría resultados similares de forma esporádica (marcada para hacer que suceda más rápido, se pueden agregar matrices sin referencias ficticias), B. Utilizo las herramientas para investigar el problema, pero las sugerencias que recibo de las herramientas no coinciden. C. El objetivo de esta pregunta es obtener indicios de qué es una buena práctica, qué está mal con mis conclusiones sobre la lectura de DDMS, y si hay una solución alternativa. – Meymann

+1

** Una manera muy fácil de reproducir el problema en otra forma ** 1. en su clase de Actividad, agregue estático {byte dummy [] = new byte [4096]; } forzar al montón a expandirse (y eliminar dudas). 2. crea un ViewFlipper. 3. Agregue aproximadamente 10 ImageView donde cada uno se refiere a un mapa de bits 600x800 dibujable. 4. Cuando falla, mira el DDMS. – Meymann

Respuesta

12

Creo que no hay nada especial en su caso. Simplemente no hay suficiente memoria. No puede tener varios mapas de bits 600x800 en memoria, consumen demasiada memoria. Deberías guardarlos en SD y cargarlos en la memoria a pedido. Creo que eso es exactamente lo que haces.

Algo que debe tener en cuenta: DDMS muestra el consumo de la memoria del montón de Java. Pero también hay memoria nativa que no se muestra en DDMS. Y los mapas de bits, por lo que yo sé, se crean en la memoria nativa. Entonces DDMS es solo una mala herramienta para rastrear estos problemas de memoria. Solo necesita asegurarse de liberar su memoria, que Garbage Collector recopila las imágenes una vez que ya no las necesita.

Garbage Collector trabaja en su propio horario. Es por eso que debes llamar al método Bitmap.recycle() en mapas de bits que ya no necesitas. Este método libera exactamente la memoria nativa que se te acaba. De esta manera no depende de GC y puede liberar la mayor cantidad de memoria lo antes posible.

Antes que nada, debe asegurarse de no filtrar mapas de bits.

Aquí es un buen post en las asignaciones de memoria, que puede ayudar a cavar más profundo

+0

Seguro, la carga lenta de SD es lo que he estado haciendo. Esto es lo que Gallery está haciendo. Sin embargo, estoy usando un caché de 4 ranuras para acelerarlo. Aún así, hay tres cosas que necesito entender: 1. ¿Cómo obligo a Gallery a enviar imágenes sin usar lo antes posible? 2. ¿Cómo es que no es suficiente? 800x600x4imagesx3bytesPerPixel = 5M que es más pequeño que 16M. 3. Me pregunto por qué es tan esporádico. A veces funciona, y vuela en diferentes lugares todo el tiempo. Gracias – Meymann

+3

Al usar la memoria caché de 4 ranuras, puede llamar a recycle() cuando esté eliminando imágenes antiguas de esta memoria caché. – Fedor

+1

Cuando se llama a getView en el adaptador se le pasa convertView. En realidad, es una vista que se recicla. La instancia de mapa de bits que se muestra en esta vista ya no se necesita. Así que supongo que puedes mostrar un mapa de bits en ese ImageView y reciclarlo. Eso es algo cercano al enfoque ASAP. – Fedor

5
No

seguro si es una opción para usted, pero ¿ha intentado supersampling imágenes Strange out of memory issue while loading an image to a Bitmap object?

+1

Gracias, pero me temo que no servirá ... Mata la calidad de la imagen, ya que permite que el decodificador sea más "descuidado". En consecuencia, el texto y los pequeños fragmentos de la imagen se manchan mucho ... Al igual que JPEG sabe cómo untar ... En realidad, se asemeja a lo que sucede cuando se toma la miniatura JPEG EXIF ​​y se cambia el tamaño a escala completa. Gracias – Meymann

0

que también se enfrentó tema par similar de semanas atrás y lo resolví reduciendo el tamaño de las imágenes hasta el punto óptimo. He escrito un enfoque completo en mi blog here y he cargado un proyecto de muestra completo con el código propenso de OOM contra el código OOM Proof here.

0

Ha pasado mucho tiempo desde que pregunté eso.

La respuesta debe estar dividida en 2 partes: Pre-Gingerbread: solo usa imágenes pequeñas, use submuestreo, tal vez una foto de tamaño de pantalla, y espere por siempre. Intenta asegurarte de no asignar elementos pequeños que no puedas liberar antes de obtener un mapa de bits. Antes del jengibre, la memoria para los bmps tenía que ser condicional, y no se contaba en la memoria de la máquina virtual. Siempre mire el logcat. Vea una conferencia sobre la memoria de Google IO 2011. Publicar Ginger es más fácil. Desde Honeycomb, incluso se cuentan los mapas de bits en su área de Java. No hay área jni. Utilice siempre el reciclaje para mapas de bits que no necesite. No esperes el GC.

0

La pregunta se planteó en 2010, cuando Froyo estaba fresco. Tantas cosas pasaron desde entonces. Antes de 3.0, se asignaron mapas de bits en JNI. La memoria no se muestra en las estadísticas de Dalvik. Ya no tiene que ser monolítico. Antes de 2.3, las estadísticas de memoria para JNI no estaban disponibles (llamadas de descodificación de mapas de bits JNI) en logcat. 4.4 evacuó más espacio. 5.0 el Big Bang de Arte. En 2010, Nexus One era de gama alta, con menos de 300 MB. El presupuesto para una aplicación fue de alrededor de 16 MB. Hoy en día, hay cerca de 8 veces esa memoria.

Cuestiones relacionadas