2011-10-14 27 views
13

Estoy viendo un problema bastante extraño. Básicamente, a veces las grandes asignaciones de memoria de mapas de bits fallarán, aunque aparentemente hay toneladas de memoria. Hay una serie de publicaciones que parecen hacer una pregunta similar, pero todas están relacionadas con Android pre-honeycomb. Según tengo entendido, las imágenes se asignan en montón ahora, en lugar de algo de memoria externa. De todos modos, por favor busque en este registro a continuación:Error de OutOfMemory aunque la memoria libre está disponible

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. 

Me disculpo por que incluye tanto el registro, 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 montón máximo. Luego, solicitamos una asignación especialmente grande que falla. Claramente, hay memoria más que suficiente disponible (alrededor de 15 megas). ¿Esto significa que el montón está fragmentado internamente y no hay segmentos de memoria contiguos lo suficientemente grandes como para manejar nuestra asignación? Si ese es el caso, ¿qué debería hacer? Si eso no es así, ¿entonces qué?

Gracias de antemano.

+0

¿Está utilizando softreferences? – Raunak

+0

No directamente, aunque la 3ª línea dice algo acerca de SoftReferences. Estoy usando un caché basado en colecciones de google que usa WeakReferences hasta donde yo sé. – EightyEight

+0

Estoy viendo el mismo problema en un S3 en OS 4.1.2, con aparentemente aún más memoria libre disponible: 10-02 14: 28: 22.458 5333 9044 D dalvikvm: GC_BEFORE_OOM liberó 0K, 54% gratis 30274K/65543K, pausó 120ms, total 120ms –

Respuesta

16

El comportamiento extraño se debe a que los mapas de bits se asignan en el montón nativo y no en la basura recolectada, pero Android solo puede rastrear objetos en el montón recogido 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.

Volviendo a la pregunta, es probable que su problema sea que los mapas de bits cargados manualmente no se hayan liberado adecuadamente. Un problema típico es que alguna devolución de llamada permanece establecida o la vista sigue refiriendo 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() sobre ellos cuando ya no los necesite, lo que liberará el mapa de bits de la memoria nativa para que el el recolector de basura podrá hacer su trabajo como debería. (El GC solo ve objetos en el montón del GC, y no tiene ningún objeto para liberar la memoria del montón nativo, y en realidad ni siquiera le importa).

Tengo este pequeño bebé a 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(); 
} 
+0

No estoy seguro de que sea correcto para honeycomb hege. Las imágenes se asignan en el montón de aplicaciones, creo, en función de esta conversación de Google IO 2011 (diapositiva 18 http://static.googleusercontent.com/external_content/untrusted_dlcp/www.google.com/en//events/io/2011/ static/presofiles/memory_management_for_android_apps.pdf). Anecdóticamente, veo crecer mi montón de aplicaciones cuando asigno un mapa de bits grande y me encojo cuando lo lanzo (con el código similar al que publicaste)> – EightyEight

+1

Yap, te lo he dicho :) "Desde Android 2.2 (o 2.3 quizás)) esto ha cambiado ... " – hege

+1

Lamentablemente, esto no parece ser suficiente, no estoy seguro de por qué. El reciclaje/etc no parece tener un efecto en el tamaño del montón (al menos en este dispositivo 4.0). –

2

las imágenes se descargan desde la web, cada uno que van desde 300K a 500K en tamaño, y se almacena en un ArrayList de dibujables.

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 * alto * 4 bytes por imagen para las imágenes ARGB normales. (ancho y alto en px).

Los mapas de bits consumen montón nativo, que generalmente no se muestra en un hprof. El hprof solo debe mostrarle la cantidad de objetos, es decir, BitmapDrawables o Bitmaps que quedan.

utilizo este código en mi aplicación a la salida de memoria utilizado actualmente utilizado por la aplicación y el montón orígenes:

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 muchos temas sobre este tema aquí.

Aquí es también una diapositiva útiles por Romain Guy (Android ingeniero marco) sobre las referencias suaves, referencias débiles, cachés simples, manejo de imagen: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

+0

Gracias por la respuesta Mehul. Lamentablemente, todos sus recursos son anteriores a Honeycomb. Examinaré tu código fuente – EightyEight

0

sí la el error "sin memoria" ocurre cuando estamos trabajando con un mapa de bits de gran tamaño. 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(); 
+1

¿Así que estás volviendo a escalar la imagen cuando obtienes un OOM? Supongo que es una solución potencial, pero no responde mi pregunta original. – EightyEight

0

El sistema no reajusta los tamaños de montón. Su aplicación tiene un espacio de almacenamiento (semi) predefinido. Los tamaños de montón se pueden ajustar en algunas ROM personalizadas. Sin embargo, esto no está realmente relacionado con tu problema.

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

+1

El número entre paréntesis es el ID del proceso, por lo que las estadísticas informadas son por proceso, no por todo el sistema. Estoy intentando resolver esta incongruencia. Dalvik informa que el 30% de la memoria disponible y la siguiente línea indica que la memoria se ha agotado. Extraño. – EightyEight

Cuestiones relacionadas