Error de OutOfMemory aunque la memoria libre está disponible

Estoy viendo un problema bastante extraño. Esencialmente, a veces grandes asignaciones de memoria de mapa de bits fallarán, aunque aparentemente hay toneladas de memoria. Hay una serie de mensajes que parecen hacer una pregunta similar, pero todos ellos están relacionados con pre-nido de abeja androide. Mi comprensión es que las imágenes se asignan en montón ahora, en lugar de alguna memoria externa. De todos modos, por favor vea este registro a continuación:

10-14 13:43:53.020: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 40.637MB for 942134-byte allocation 10-14 13:43:53.070: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 126K, 11% free 41399K/46343K, paused 31ms 10-14 13:43:53.130: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 920K, 13% free 40478K/46343K, paused 30ms 10-14 13:43:53.180: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1026K, 13% free 40479K/46343K, paused 30ms 10-14 13:43:53.250: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 931K, 12% free 41193K/46343K, paused 31ms 10-14 13:43:53.250: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 41.313MB for 1048592-byte allocation 10-14 13:43:53.280: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 11% free 42217K/47431K, paused 31ms 10-14 13:44:01.520: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 3493K, 15% free 40646K/47431K, paused 3ms+9ms 10-14 13:44:08.130: DEBUG/dalvikvm(31533): GC_EXPLICIT freed 16461K, 47% free 25527K/47431K, paused 3ms+6ms 10-14 13:44:09.150: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1007K, 45% free 26191K/47431K, paused 35ms 10-14 13:44:09.160: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 29.334MB for 3850256-byte allocation 10-14 13:44:09.200: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 0K, 37% free 29951K/47431K, paused 2ms+4ms 10-14 13:44:11.970: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1878K, 38% free 29784K/47431K, paused 37ms 10-14 13:44:12.410: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 62K, 36% free 30441K/47431K, paused 32ms 10-14 13:44:12.440: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 32% free 32325K/47431K, paused 32ms 10-14 13:44:12.440: INFO/dalvikvm-heap(31533): Forcing collection of SoftReferences for 3850256-byte allocation 10-14 13:44:12.480: DEBUG/dalvikvm(31533): GC_BEFORE_OOM freed 124K, 33% free 32200K/47431K, paused 37ms 10-14 13:44:12.480: ERROR/dalvikvm-heap(31533): Out of memory on a 3850256-byte allocation. 

Pido disculpas por incluir tanta explotación forestal, espero que sea relevante. La forma en que lo leo es que el sistema reajusta continuamente el tamaño del montón hasta que finalmente alcanza el máximo del montón. Entonces, pedimos una asignación especialmente grande que falla. Es evidente que hay más que suficiente memoria disponible (unos 15 megas). ¿Significa esto que el montón está internamente fragmentado y no hay segmentos de memoria contiguos lo suficientemente grandes para manejar nuestra asignación? Si ese es el caso, ¿qué debo hacer? Si no es eso, ¿entonces qué?

Gracias por adelantado.

4 Solutions collect form web for “Error de OutOfMemory aunque la memoria libre está disponible”

El comportamiento extraño se debe a que los mapas de bits se asignan en el montón nativo y no en la basura recopilada, pero Android sólo puede realizar el seguimiento de objetos en el montón de recolección de basura. Desde Android 2.2 (o 2.3 quizás) esto ha cambiado y los mapas de bits asignados también son visibles si realiza un volcado de pila.

De nuevo a la pregunta, su problema es muy probablemente que los mapas de bits cargados manualmente no se liberan apropiadamente. Un problema típico es que algunos callback permanece establecido o la vista todavía está referenciando el mapa de bits. El otro problema común es que si carga mapas de bits grandes manualmente (no como un recurso), tendrá que llamar a recycle () en ellos cuando ya no lo necesite, lo que liberará el mapa de bits de la memoria nativa para que la basura Colector será capaz de su trabajo como debería. (El GC sólo ve objetos en el montón de GC, y no tiene ningún objeto que libre para liberar memoria del montón nativo, y en realidad ni siquiera se preocupa por él).

Tengo a este pequeño bebé en la mano todo el tiempo:

 public static void stripImageView(ImageView view) { if ( view.getDrawable() instanceof BitmapDrawable ) { ((BitmapDrawable)view.getDrawable()).getBitmap().recycle(); } view.getDrawable().setCallback(null); view.setImageDrawable(null); view.getResources().flushLayoutCache(); view.destroyDrawingCache(); } 

Las imágenes se obtienen de la Web, cada una de 300K a 500K de tamaño, y se almacenan en una lista de dibujos.

El tamaño del archivo kb de la imagen que está cargando desde la web no es directamente relevante. Dado que se convierten en mapas de bits, debe calcular el ancho * altura * 4 bytes por imagen para las imágenes de ARGB normales. (Ancho y alto en px).

Los mapas de bits consumen montón nativo, que normalmente no se muestra en un hprof. El hprof sólo debe mostrar el número de objetos, es decir, BitmapDrawables o Bitmaps que quedan.

Yo uso este código en mi aplicación para generar la memoria utilizada actualmente utilizada por la aplicación y el montón nativo:

 public static void logHeap(Class clazz) { Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576)); Double available = new Double(Debug.getNativeHeapSize())/1048576.0); Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0); DecimalFormat df = new DecimalFormat(); df.setMaximumFractionDigits(2); df.setMinimumFractionDigits(2); Log.d(APP, "debug. ================================="); Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]"); Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)"); System.gc(); System.gc(); // don't need to add the following lines, it's just an app specific handling in my app if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) { android.os.Process.killProcess(android.os.Process.myPid()); } } 

Que llamo al iniciar o terminar una actividad durante el desarrollo.

 logHeap(this.getClass()); 

Aquí hay algunos enlaces informativos – generalmente hay un montón de temas sobre este tema aquí.

  • Bitmaps en Android
  • Android: Eclipse MAT no parece mostrar todos los objetos de mi aplicación

Aquí también hay una útil diapositiva de Romain Guy (ingeniero de Android Framework) sobre referencias suaves, referencias débiles, cachés sencillos, manejo de imágenes: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

El sistema no reajusta los tamaños de montón. Su aplicación tiene un espacio de montón (semi-) predefinido. Los tamaños de montón pueden ajustarse en algunas ROM personalizadas. Esto no es realmente pertinente a su problema sin embargo.

El orden de la recolección de basura que está viendo parece sugerir que en el momento de la GC_BEFORE_OOM sucede que su aplicación se está quedando sin su espacio de montón. Las estadísticas reportadas por el maderero Dalvik podrían ser de todo el sistema.

Sí el error "fuera de memoria" ocurre cuando estamos trabajando con el tamaño grande Bitmap. Para eso tengo una solución, con Decodificación de la imagen podemos evitar fácilmente este error.

 BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; FileInputStream fis = new FileInputStream(files.getAbsolutePath()); BitmapFactory.decodeStream(fis, null, o); fis.close(); final int REQUIRED_SIZE=70; int width_tmp=o.outWidth, height_tmp=o.outHeight; int scale=1; while(true){ if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) break; width_tmp/=2; height_tmp/=2; scale*=2; } BitmapFactory.Options op = new BitmapFactory.Options(); op.inSampleSize = scale; fis = new FileInputStream(files.getAbsolutePath()); bitmap[i] = BitmapFactory.decodeStream(fis, null, op); fis.close(); 
  • ALPHA_8 bitmaps y getPixel
  • ¿Qué hay de malo en este método de cálculo inSampleSize para Bitmaps? Sin memoria
  • Mapbox GL utilizando mapas externos
  • Medidas de altura de StaticLayout fuera
  • Convertir imagen a mapa de bits en android
  • ¿Por qué setImageBitmap no tiene efecto en ImageButton personalizado?
  • Representación de mapa de bits de Eclipse en el depurador
  • Cómo dibujar en Bitmap en android?
  • Aumentar la memoria asignada a la aplicación
  • Decodificación de la imagen SVG a mapa de bits
  • GetDrawingCache () devuelve null
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.