2012-05-20 11 views
28

Soy bastante nuevo en el desarrollo de Android y parece que no puedo entender la excepción de Java Out of Memory. Sé que significa que mi aplicación ha superado el presupuesto de VM, pero después de buscar en Google esto muchas veces, todavía no entiendo este concepto. Me temo que mi aplicación utiliza demasiada memoria porque tengo seis selectores de botones por pantalla con dos mapas de bits para cada selector, que son alrededor de 20 kb cada uno de acuerdo con la pestaña de propiedades. En mi rooteado G2x, configuré el presupuesto de VM en 12mb, reinicié mi teléfono y ejecuté mi aplicación sin ningún problema. Estoy desentrañando dibujables en cada onDestroy() y sugiero que el GC también se ejecute aquí. Después de usar la aplicación por un tiempo en el emulador, hago clic en "Causa GC" en mi pantalla DDMS y los resultados son ID = 1, Heap Size 6.133 MB, Asignado 2.895MB, Free 3.238 MB,% Used 47.20, # Objects 52,623.Android Understanding Heap Sizes

Aquí es donde no entiendo lo que está sucediendo, mi emulador está configurado en 24MB de VM. ¿Dónde está ese número? El verdadero problema que tengo es que si configuro el emulador en 16 MB de VM, mi aplicación se bloquea en la segunda actividad con la excepción de Memoria agotada. ¿Cómo es que no se cuelga en mi teléfono con la VM configurada en 12 MB o en mi viejo teléfono HTC Magic con 12 MB de inventario de VM? ¿Ustedes también piensan que mi aplicación está ocupando demasiada memoria? No tengo idea si esos números de DDMS son buenos o no. Gracias por tu tiempo.

En cuanto a mi código, tengo todas las imágenes especificadas en los diseños XML. No realizo ninguna actividad programática con ellas, excepto para agregar oyentes. He encontrado este fragmento de código en aquí y he añadido a todas las actividades que tengo ...

@Override 
protected void onDestroy() { 
    super.onDestroy(); 

    unbindDrawables(findViewById(R.id.myRootLayout)); 
    System.gc(); 
} 

private void unbindDrawables(View view) { 
    if (view.getBackground() != null) { 
     view.getBackground().setCallback(null); 
    } 
    if (view instanceof ViewGroup && !(view instanceof AdapterView)) { 
     for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { 
      unbindDrawables(((ViewGroup) view).getChildAt(i)); 
     } 
     ((ViewGroup) view).removeAllViews(); 
    } 
} 

Por lo demás todo lo que hago es añadir onClickListeners a los botones que tienen los fondos del png. Me gustaría aprender cómo especificar los fondos de los botones de forma programática, pero necesito que las funciones del selector estén centradas, al presionar, no enfocadas, pero presionadas, etc., para que los fondos de los botones cambien según la interacción del usuario. He revisado los documentos acerca de esto, pero parece abrumador, es por eso que pensé que comenzaría aquí con los conceptos básicos de la administración de Heaps y me abriría camino hasta especificar selectores en el código. Esto puede no tener sentido, pero ¿hay una cantidad "sana" de asignación de memoria que una aplicación podría asignar sin acercarse a la excepción de Memoria agotada? Por ejemplo, si una aplicación asignó 6MB, debería estar bien, pero 8 MB la impulsarían, ¿hay límites como ese en la asignación de memoria? Gracias de nuevo Alex Lockwood por su respuesta Voy a leer y volver a leerlo hasta que esto tenga sentido

Respuesta

50

Cuando establece el presupuesto de la VM en su emulador/dispositivo, lo que está haciendo le está diciendo al montón el tamaño máximo que está permitido. En tiempo de ejecución, el almacenamiento crece de forma dinámica a medida que la VM Dalvik solicita la memoria del sistema desde el sistema operativo. La máquina virtual de Dalvik normalmente comienza asignando un montón relativamente pequeño. Luego, después de ejecutar cada GC, se comprueba para ver cuánta memoria Heap libre hay. Si la proporción de montón libre a montón total es demasiado pequeña, la VM Dalvik agregará más memoria al montón (hasta el tamaño de montón máximo configurado).

Dicho esto, la razón por la que no está viendo "24 mb" en su pantalla DDMS es porque el montón no ha crecido hasta su tamaño máximo. Esto permite a Android hacer un buen uso de la ya pequeña cantidad de memoria que está disponible en los dispositivos portátiles.

En cuanto a por qué su aplicación se bloquea en el emulador y no en su teléfono, parece extraño (¿está seguro de que los números son correctos?). Sin embargo, debe tener en cuenta que la memoria se gestiona de forma dinámica y que la utilización total de la memoria se determina en función de una serie de factores externos (la velocidad/frecuencia con la que se realiza la recolección de basura, etc.).

Finalmente, por las razones que mencioné anteriormente, sería difícil determinar con certeza qué tan bien administra la memoria la aplicación en función de la única línea de información que proporcionó anteriormente. Realmente necesitaríamos ver algo de tu código.Sin embargo, vale la pena preocuparse por OutOfMemoryError s, así que definitivamente consideraré el uso de la memoria de su aplicación. Una cosa que podría considerar es muestrear sus imágenes de mapa de bits en tiempo de ejecución con llamadas al inSampleSize usando la clase BitmapFactory. Esto puede ayudar a reducir la cantidad de memoria requerida para cargar sus mapas de bits dibujables. O eso o podrías reducir la resolución de tus drawables (aunque 20 Kb cada uno me suena bien).

+3

+1, respuesta muy informativa! –

+0

¡Muchas gracias! Aprendí mucho de eso. Estoy agregando algo de información al pie de la pregunta –

+0

Comentario de resolución "20 kb cada uno suena bien" - AFAIK, no importa cuál es * el tamaño del archivo * (que será más pequeño para las imágenes que se comprimen mejor), importa lo que * DIMENSIONS * son; en la memoria, para el color completo, tomará 4B por píxel, ¿verdad? Por lo tanto, es importante cambiar el tamaño de la imagen cuando la cargue [documentos de android - Cargue una versión reducida en la memoria], según el tamaño real necesario para el dispositivo actual. – ToolmakerSteve

16

Incluso si su aplicación no alcanza el límite de almacenamiento dinámico de "24mb" (varía dentro de los dispositivos), es posible que todavía tenga bloqueos porque Android demora un poco para hacer crecer el espacio de almacenamiento dinámico para su aplicación.

En mi situación estaba creando y volcando varias imágenes en poco tiempo.

A menudo recibía OutOfMemoryError.

Parece que Android no fue lo suficientemente rápido como para hacer crecer el espacio de almacenamiento dinámico para mi aplicación.

Creo que he resuelto este problema utilizando la configuración largeHeap en el archivo Manifest. Con esa configuración, Android deja más memoria libre cada vez que crece el montón, minimizando la posibilidad de alcanzar el límite actual.

No uso el límite de 24 mb, pero este largeHeap conf fue bastante útil.

sólo tiene que establecer largeHeap="true" en la etiqueta de su aplicación AndroidManifest.xml

<application 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" 
    android:largeHeap="true" 
    android:theme="@style/AppTheme" > 

Sin embargo, asegúrese de que está cuidado cuando se trata de imágenes, como @Alex Lockwood aconsejó.

+16

Use largeHeap = "true" con el máximo cuidado. Porque usar esto puede afectar negativamente el rendimiento de su aplicación. Porque le está diciendo al sistema que aumente el límite máximo de almacenamiento dinámico. Cuando esto suceda, tomará más tiempo para la recolección de basura. Si revisa el registro, puede ver que el tiempo de pausa del GC será mayor. Idealmente, debería estar entre 2 y 5 ms. En este caso, puede variar incluso hasta 30 a 45 ms. Por lo tanto, no establezca la propiedad del montón grande en verdadero solo porque se está quedando sin memoria. Úselo como el último paso. De lo contrario, será un golpe de rendimiento. –

+0

De hecho. En mi caso, estoy lidiando con muchas imágenes de alta calidad dinámicamente y, antes de utilizar la opción de gran tamaño, he implementado código para: - cargar imágenes muestreadas - cambiar el tamaño de las imágenes para un ajuste perfecto - eliminar imágenes no utilizadas de la memoria - almacenamiento de imágenes en caché en "disco" Todavía tenía problemas que se resolvieron solo con la opción de gran tamaño. Aún así, úselo solo después de probar todo lo demás, como @SanalVarghese aconseja. – tbraun

+0

Simplemente agregando a esto, he tenido muchos problemas con la memoria de mi aplicación, su tamaño aumenta enormemente. Puede que no se haya configurado de manera eficiente en este punto, pero debería haber sido capaz de no exceder el límite de heep. Sin embargo, nunca liberaría muchos recursos. Por alguna razón, lo hace muy bien después de aplicar la opción largeHeap. Gracias un montón. –