Cómo registrar asignaciones de Java/Kotlin

Registrar las asignaciones de Java/Kotlin te ayuda a identificar patrones de memoria no deseados que podrían estar causando problemas de rendimiento. El generador de perfiles puede mostrarte lo siguiente sobre la asignación de objetos:

  • Qué tipos se asignaron y cuánto espacio ocupan
  • El seguimiento de pila de cada asignación, incluido el subproceso
  • Fecha y hora en que se liberaron los objetos.

Debes registrar las asignaciones de memoria durante las interacciones normales y extremas del usuario para identificar con exactitud si tu código asigna demasiados objetos en poco tiempo o si asigna objetos que se fugan. Obtén más información sobre por qué debes generar perfiles para la memoria de tu app.

Cómo registrar asignaciones de Java/Kotlin

Para registrar asignaciones de Java/Kotlin, selecciona la tarea Track Memory Consumption (Java/Kotlin Allocations) en la pestaña Home del generador de perfiles. Ten en cuenta que necesitas una app depurable (usa Profiler: run 'app' as debuggable (complete data)) para registrar las asignaciones de Java/Kotlin.

De forma predeterminada, Android Studio captura todas las asignaciones de objetos en la memoria. Si tu app asigna una gran cantidad de objetos, es posible que se produzcan ralentizaciones visibles en ella durante la generación de perfiles. Para mejorar el rendimiento durante la generación del perfil, ve al menú desplegable Allocation Tracking y selecciona Sampled en lugar de Full. Cuando se muestrea, el generador de perfiles recopila las asignaciones de objetos en la memoria a intervalos regulares.

Para forzar un evento de recolección de elementos no utilizados durante la grabación, haz clic en el ícono de papelera .

Descripción general de las asignaciones de Java/Kotlin

Después de detener la grabación, verás lo siguiente:

  • El cronograma de eventos muestra los estados de actividad, los eventos de entrada del usuario y los eventos de rotación de pantalla.
  • El cronograma de uso de memoria muestra la siguiente información. Selecciona una parte del cronograma para filtrar un período determinado.
    • Un gráfico de barras apiladas del volumen de memoria que se usa en cada categoría, como se indica con el eje Y a la izquierda, y la clave de color en la parte superior
    • Una línea punteada que indica la cantidad de objetos asignados, como se indica con el eje Y a la derecha
    • Un ícono para cada evento de recolección de elementos no utilizados
  • En la pestaña Tabla, se muestra una lista de clases. El Total Count es la cantidad de asignaciones al final del período seleccionado (Allocations menos Deallocations), por lo que podría ser conveniente depurar primero las clases que tienen los valores de Total Count más altos. Si te interesa más solucionar problemas de clases en función de las asignaciones máximas durante el período seleccionado, prioriza por Asignaciones. Del mismo modo, el Remaining Size es el Allocations Size menos el Deallocations Size en bytes.
  • Cuando haces clic en una clase en la lista Tabla, se abre el panel Instancia con una lista de objetos asociados, que incluye cuándo se asignaron, cuándo se liberaron y su tamaño superficial.
  • En la pestaña Visualización, se muestra una vista agregada de todos los objetos en la pila de llamadas durante el intervalo de tiempo seleccionado. Básicamente, te muestra cuánta memoria total ocupa el registro de pila con las instancias que se muestran. La primera fila muestra el nombre del subproceso. De forma predeterminada, los objetos se apilan de izquierda a derecha según el tamaño de asignación. Usa el menú desplegable para cambiar el orden.

  • Usa el menú desplegable de la pila para filtrar por pilas específicas. Además de los filtros disponibles cuando capturas un volcado de montón, puedes filtrar las clases en el montón de JNI, que muestra dónde se asignan y liberan las referencias de la interfaz nativa de Java (JNI).

  • Usa el menú desplegable de organización para elegir cómo organizar las asignaciones. Además de las disposiciones disponibles cuando capturas un volcado de montón, puedes ordenar por pila de llamadas.

Cómo se registra la memoria

Los números que ves en la parte superior se basan en todas las páginas privadas de memoria que confirma tu app, según el sistema Android. Este registro no incluye páginas compartidas con el sistema ni otras apps. Las categorías del recuento de memoria son las siguientes:

  • Java: Es la memoria de objetos asignados desde código Java o Kotlin.
  • Nativa: Es la memoria de objetos asignados desde código C o C++.

    Incluso si no usas C++ en tu app, puedes ver memoria nativa usada aquí porque el framework de Android utiliza ese tipo de memoria para administrar varias tareas por ti; por ejemplo, cuando maneja elementos de imágenes y otros gráficos (aunque tu código esté en Java o Kotlin).

  • Gráficos: Es la memoria que se usa para las colas de búfer de gráficos con el propósito de mostrar píxeles en la pantalla, incluidas las superficies y texturas GL, entre otras opciones. Ten en cuenta que se trata de memoria compartida con la CPU, y no de memoria dedicada de la GPU.

  • Pila: Es la memoria usada tanto por las pilas nativas como por las de Java en tu app. Esto normalmente se relaciona con la cantidad de subprocesos en ejecución que tiene tu app.

  • Código: Es la memoria que tu app usa para código y recursos, como código de bytes DEX, código DEX optimizado o compilado, .Bibliotecas y fuentes de so

  • Otras: Esta categoría incluye la memoria usada por tu app que el sistema no sabe cómo categorizar.

  • Asignada: Es la cantidad de objetos Java y Kotlin asignados por tu app. No se tienen en cuenta los objetos asignados en C o C++.

Inspecciona el registro de asignación

Para inspeccionar el registro de asignaciones, sigue estos pasos:

  1. Explora la lista de clases en la pestaña Tabla para encontrar objetos que tengan valores de Asignaciones o Recuento total inusualmente grandes (según lo que estés optimizando) y que podrían experimentar fugas.
  2. En el panel Instance View, haz clic en una instancia. Según lo que corresponda a esa instancia, se abrirá la pestaña Fields o Allocation Call Stack. Usa la información de las pestañas Fields o Allocation Call Stack para determinar si las instancias son realmente necesarias o duplicaciones innecesarias.

Haz clic con el botón derecho en cualquier entrada de la lista para ir al código fuente pertinente.

Cómo ver referencias globales de JNI

La interfaz nativa de Java (JNI) es un framework que permite que el código Java y el código nativo se llamen entre sí. Las referencias de JNI se administran manualmente con código nativo, por lo que es posible que ocurran problemas, incluidos los siguientes:

  • Los objetos Java que utiliza el código nativo se mantienen activos durante demasiado tiempo.
  • Algunos objetos del montón de Java pueden llegar a ser inalcanzables si se descarta una referencia de JNI sin primero borrarla de forma explícita.
  • Se agotó el límite de referencias globales de JNI.

Para solucionar estos problemas, selecciona Ver JNI heap en el generador de perfiles para examinar todas las referencias globales de JNI y filtrarlas por tipos de Java y pilas de llamadas nativas. Haz clic con el botón secundario en un campo de instancia en la pestaña Fields y selecciona Go to instance para ver la pila de llamadas de asignación pertinente.

En la pestaña Allocation Call Stack, se muestra dónde se asignan y liberan las referencias de JNI en tu código.

Para obtener más información sobre JNI, consulta estas sugerencias relacionadas.