Grandes requisitos de memoria en Android: ¿qué hacer?

Hola queridos lectores.

Hoy traemos a su atención un pequeño material sobre el uso competente de la memoria en Android .



¡Disfruta leyendo!

Este artículo se centra en las técnicas básicas para administrar el uso de la memoria en las aplicaciones, por ejemplo, navegadores, editores de fotos y visores de PDF, en los que se realizan grandes solicitudes de memoria.

Primero, una pequeña teoría


La mayoría de las aplicaciones de Android se ejecutan sobre el tiempo de ejecución ( ART ), que ha reemplazado a la máquina virtual Dalvik ahora obsoleta. ART y Dalvik son similares a la Java Virtual Machine (JVM), con la que comparten principios de diseño similares. Usan dos espacios separados para almacenar datos de la aplicación: la pila y el montón.

Memoria de

pila La memoria de pila en Java se usa para almacenar variables locales (tipos primitivos y referencias de objeto). Cada hilo de Java tiene su propia pila separada. La memoria de pila es relativamente pequeña en comparación con la memoria de montón. El tamaño de la pila Java de Dalvik suele ser de 32 KB para el código Java y 1 MB para el código nativo (C ++ / JNI). Una pila unificada para Java y C ++ ha aparecido en ART, cuyo tamaño es de aproximadamente 1 MB.

Cuando la aplicación selecciona toda la memoria de la pila hasta el límite, se produce un error StackOverflowError. Las razones más probables por las que se puede alcanzar un límite de pila es la recursión infinita o una llamada a un método demasiado profundo. Las referencias a la memoria de la pila siempre se hacen en el orden LIFO (orden de llegada). Cada vez que se llama a un método, se empuja un nuevo marco a la pila con las variables locales de este método. Cuando se completa el método, su marco se saca de la pila y cualquier valor resultante posible se envía de vuelta a la pila. Entonces, el primer problema (recursión infinita) es un error que es fácil de solucionar, pero el segundo requiere una refactorización, que consiste en desplegar llamadas a métodos recursivos y convertirlas en un bucle.

Memoria del montón

La memoria virtual en Java es utilizada por una máquina virtual para asignar objetos. Cada vez que se crea un objeto, sucede en el montón. Las máquinas virtuales, como JVM o ART, recolectan basura regularmente, eliminan todos los objetos a los que ya no se hace referencia y, por lo tanto, liberan memoria para asignar nuevos objetos.
Para garantizar la usabilidad, Android limita estrictamente los tamaños de almacenamiento dinámico para cada aplicación en ejecución. El límite de tamaño de almacenamiento dinámico varía de un dispositivo a otro y depende de la cantidad de RAM en ese dispositivo. Si su aplicación alcanza el tamaño máximo de almacenamiento dinámico e intenta asignar más memoria, se genera un error OutOfMemoryErrory la aplicación finaliza. Veamos algunos ejemplos para ayudar a evitar esta situación.

Análisis de memoria de montón


La herramienta más importante para comprender los problemas de memoria en sus aplicaciones y comprender cómo se utiliza la memoria es el generador de perfiles de memoria disponible en Android Studio.

Esta herramienta visualiza cuánta memoria consume su aplicación con el tiempo. Puede tomar instantáneas del montón de Java en una aplicación en ejecución, registrar las operaciones de asignación de memoria y realizar un seguimiento del montón o de este historial de asignación de memoria en una interfaz de usuario potente.

Una sesión típica del generador de perfiles de memoria debería verse así:

  • Observamos las asignaciones de memoria más frecuentes y los pasajes del recolector de basura para identificar posibles problemas de rendimiento.
  • , , , , , . , . , , PdfActivity PSPDFKit .
  • , . , . – , , .


Los recolectores de basura modernos son obras complejas de arte tecnológico, el resultado de muchos años de investigación y desarrollo, en los que participaron cientos de personas, desde académicos hasta desarrolladores profesionales. Sin embargo, todavía tiene que estar alerta para evitar pérdidas de memoria.

Una solución ejemplar para detectar pérdidas de memoria es la biblioteca LeakCanary . Emite notificaciones automáticamente cuando está en su ensamblaje de prueba (desarrollo de desarrollo), lo que le proporciona la tasa de seguimiento de fugas en la interfaz de usuario de este programa. Puede (y debe) integrarlo hoy, ¡especialmente porque no es difícil!

Es especialmente fácil provocar pérdidas de memoria cuando se trabaja con ciclos de vida complejos de actividades o fragmentos de Android. Esto sucede a menudo en los puntos donde los desarrolladores tienen fuertes referencias a contextos de IU u otros objetos específicos de UI en la tarea en segundo plano o en variables estáticas. Una forma de provocar tales demoras es girar activamente el dispositivo al probar su aplicación.

Libere memoria en respuesta a eventos


Android puede requerir que la aplicación asigne memoria, o simplemente obligarla a terminar cuando la memoria necesita ser liberada para realizar tareas más críticas. Antes de que esto suceda, el sistema le permitirá proporcionar toda la memoria que no necesita. En su actividad, deberá implementar una interfaz ComponentCallbacks2. En este caso, cada vez que su sistema se quede sin memoria, se realizará una llamada a su método onTrimMemory()y podrá liberar memoria o deshabilitar funciones que no funcionarán en tales condiciones de escasez de memoria.

Entonces, tales devoluciones de llamada se manejan en la aplicación PSPDFKit. La aplicación PSPDFKit se diseñó con el cálculo del uso activo de la memoria para el almacenamiento en caché, de modo que la aplicación se ejecute de la manera más fluida posible. Inicialmente, no se sabe cuánta memoria hay disponible en el dispositivo, por lo que PSPDFKit se adapta a la situación y limita el uso de la memoria cuando recibe notificaciones de que no hay suficiente memoria. Por lo tanto, las aplicaciones integradas con PSPDFKit funcionan incluso en dispositivos de baja tecnología, pero con un rendimiento reducido debido al hecho de que el almacenamiento en caché está desactivado.

Gran pila


Una de las soluciones front-end para hacer frente a los altos requisitos de memoria es solicitar un gran grupo de Dalvik para su aplicación. Para hacer esto, puede agregar android:largeHeap="true"a la etiqueta <aplicación> en el archivo AndroidManifest.xml.

Si la propiedad largeHeapse establece en valor true, Android creará todos los procesos para su aplicación con un gran montón. Esta configuración está destinada solo a aquellas aplicaciones que, por su naturaleza, no pueden funcionar sin ella, es decir, utilizan recursos voluminosos que deben caber simultáneamente en la memoria.

Se desaconseja encarecidamente utilizar un montón grande si solo desea elevar el techo para un posible uso de memoria. El uso de la memoria siempre debe optimizarse, porque incluso una gran cantidad de su aplicación puede no ser suficiente cuando se trabaja en un dispositivo débil con poca memoria.

Compruebe cuánta memoria puede usar su aplicación


Nunca está de más comprobar cuán grande es el montón de su aplicación y adaptar dinámicamente su código y las capacidades disponibles a estos límites de memoria. Puede verificar el tamaño máximo de almacenamiento dinámico directamente en tiempo de ejecución utilizando métodos getMemoryClass()o getLargeMemoryClass()(cuando se habilita un almacenamiento dinámico grande).

Android incluso admite dispositivos con solo 512 MB de RAM. ¡Asegúrese de no ignorar los dispositivos de baja tecnología! Usando el métodoisLowRamDevice()Puede verificar si su aplicación se está ejecutando en un dispositivo en el que no hay suficiente memoria disponible. El comportamiento exacto de este método depende del dispositivo, pero generalmente es cierto en dispositivos con menos de 1 GB de RAM. Debe asegurarse de que su aplicación funciona correctamente en estos dispositivos y deshabilitar todas las funciones que utilizan una gran cantidad de memoria en ellos.

Lea más sobre cómo funciona Android en dispositivos con una pequeña cantidad de memoria, puede leer aquí ; Consejos de optimización adicionales también se proporcionan aquí.

Use estructuras de datos optimizadas


En muchos casos, las aplicaciones usan demasiada memoria por la simple razón de que no usan las estructuras de datos más apropiadas.

Las colecciones Java no pueden almacenar tipos primitivos eficientes y requieren empaquetar sus claves y valores. Por ejemplo, HashMapcon las claves enteras se deben reemplazar por las optimizadas SparseArray. En última instancia, siempre puede usar matrices sin formato en lugar de colecciones, y esta es una gran idea si su colección no es redimensionable.

Otras estructuras de datos que son ineficientes en términos de uso de memoria incluyen varias serializaciones. Sí, de hecho, los formatos XML o JSON son convenientes de usar, puede reducir el uso de memoria si trabaja con un formato binario más eficiente, por ejemplo, búferes de protocolo.

Todos estos ejemplos, con estructuras de datos optimizadas para ahorrar memoria, son solo sugerencias. Al igual que con la refactorización, primero debe encontrar la fuente de los problemas y luego pasar a tales optimizaciones de rendimiento.

Prevenir la barajadura de memoria


Las máquinas virtuales Java / Android asignan objetos muy rápidamente. La recolección de basura también es muy rápida. Sin embargo, al asignar una gran cantidad de objetos en un corto período de tiempo, puede encontrar un problema llamado "pérdida de memoria". En este caso, la máquina virtual no tendrá tiempo para asignar objetos a este ritmo, y el recolector de basura los eliminará, y la aplicación comenzará a ralentizarse y, en casos extremos, incluso usará toda la memoria.

El principal problema en el territorio de Android en este caso es que no controlamos cuándo ocurrirá la recolección de basura. Potencialmente, esto puede generar problemas: por ejemplo, el recolector de basura funciona exactamente en el momento en que se desarrolla la animación en la pantalla y superamos el umbral de 16 ms, lo que garantiza una visualización fluida de los fotogramas. Por lo tanto, es importante evitar la asignación excesiva de memoria en el código.

Un ejemplo de una situación que conduce a la barajadura de memoria es la asignación de objetos grandes, por ejemplo, Paint dentro del método de onDraw()presentación. En este caso, se crean rápidamente muchos objetos y puede comenzar la recolección de elementos no utilizados, lo que puede afectar negativamente el rendimiento de esta vista. Como se indicó anteriormente, siempre debe monitorear el uso de memoria para evitar tales situaciones.

Conclusión


La memoria de acceso aleatorio (RAM) en dispositivos móviles puede ser un recurso muy limitado. Asegurar el uso eficiente de la memoria en la aplicación es especialmente importante si su aplicación funciona con objetos relativamente grandes, por ejemplo, gráficos de trama (visores de PDF, navegadores web, editores de fotos) o archivos multimedia de gran tamaño (editores de audio o video). Siguiendo estos consejos, aprenderá cómo crear aplicaciones de alta calidad que funcionarán a un nivel aceptable, incluso en los dispositivos más potentes.

All Articles