¿Qué herramientas y métodos de Android funcionan mejor para encontrar pérdidas de memoria / recursos? [cerrado]

152

Tengo una aplicación de Android desarrollada, y estoy en el punto de desarrollo de una aplicación de teléfono donde todo parece estar funcionando bien y quieres declarar la victoria y enviar, pero sabes que solo tiene que haber un poco de pérdida de memoria y recursos ahí; y solo hay 16 MB de almacenamiento dinámico en Android y es aparentemente sorprendentemente fácil de filtrar en una aplicación de Android.

He estado buscando y hasta ahora solo he podido obtener información sobre 'hprof' y 'traceview' y ninguno de los dos ha recibido muchas críticas favorables.

¿Qué herramientas o métodos has encontrado o desarrollado y te gustaría compartir tal vez en un proyecto de SO?

jottos
fuente
3
¿Puedo votar para que Р̀СТȢѸ́ФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ cambie su nombre a algo legible por humanos?
JPM
1
¿estaría bien volver a abrir esta pregunta si eliminamos la palabra "Herramientas de Android" del título de la pregunta? Las respuestas encontradas aquí fueron bastante útiles para resolver mis problemas de pérdida de memoria
k3b
Bien, entonces tengo un usuario que cambia constantemente entre actividades, lo que significa que pueden haber cambiado 15 actividades en 20 segundos. ¿Podría ser eso la causa de un error de falta de memoria? ¿Qué debo hacer para arreglarlo? ¡Gracias!
Ruchir Baronia
1
No puedo proporcionar esto como respuesta porque la pregunta se ha cerrado, sin embargo, recomendaría echar un vistazo a Leak Canary . Solo usa tu aplicación, abre y cierra actividades y deja que la biblioteca haga su trabajo. Incluso le informará sobre dónde ocurrió la fuga. Simplemente dé tiempo al analizador de fugas para que haga su trabajo después de que ocurra la fuga; por lo general, toma alrededor de 2 minutos o más hasta que se encuentra la fuente de la fuga. Después de eso, se lo presentará perfectamente en la aplicación. ¡No se necesitan herramientas adicionales!
ubuntudroid

Respuestas:

90

Uno de los errores más comunes que encontré al desarrollar aplicaciones de Android es el error "java.lang.OutOfMemoryError: el tamaño del mapa de bits supera el presupuesto de VM". Encontré este error con frecuencia en actividades que utilizan muchos mapas de bits después de cambiar la orientación: la Actividad se destruye, se crea nuevamente y los diseños se "inflan" desde el XML que consume la memoria de VM disponible para mapas de bits.

El recolector de basura no asigna correctamente los mapas de bits en el diseño de la actividad anterior porque han cruzado referencias a su actividad. Después de muchos experimentos, encontré una solución bastante buena para este problema.

Primero, establezca el atributo "id" en la vista principal de su diseño XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Luego, en el método onDestroy () de su Actividad, llame al método unbindDrawables () pasando una referencia a la Vista principal y luego haga un System.gc ()

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

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


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

Este método unbindDrawables () explora el árbol de vistas de forma recursiva y:

  1. Elimina devoluciones de llamada en todos los elementos dibujables de fondo
  2. Elimina childs en cada grupo de vista
HP android
fuente
3
Buena solución a un problema común.
Mientras-E
9
Esto no funciona para las subclases de AdapterView (ListView, GridView, etc.).
Arjun
@Arjun Sí ... no funciona para las subclases de AdapterView. Para eso necesitas manejar la excepción. Descansa, funciona bien. Eso es lo que uso en mi código y funciona bien. Espero que esto ayude.
HP android
@ hp.android Al "manejar esto en la excepción" ¿tiene un ejemplo de cómo se vería? Pregunto porque tengo páginas de imágenes en mi PageAdaptery estoy trabajando por este error :(
Jacksonkr
44
@Jackson solo cambia la condición: if (ver instancia de ViewGroup &&! (Ver instancia de AdapterView)) esto eliminará la excepción que está obteniendo para Adapter
hp.android
28

Principalmente para viajeros de Google del futuro:

Lamentablemente, la mayoría de las herramientas de Java no son adecuadas para esta tarea, ya que solo analizan el JVM-Heap. Sin embargo, todas las aplicaciones de Android también tienen un montón nativo, que también debe ajustarse dentro del límite de ~ 16 MB. Por lo general, se usa para datos de mapa de bits, por ejemplo. Por lo tanto, puede ejecutar fácilmente errores de falta de memoria a pesar de que su JVM-Heap se relaje alrededor de 3 MB, si usa muchos elementos extraíbles.

Timo Ohr
fuente
66
a partir Android 3.0 (Honeycomb) dibujables se almacenan en el montón
Gu1234
@Timo, entonces, ¿qué usarías para detectar fugas en el montón nativo?
sydd
1
Pruebas, muchas pruebas. El problema es que ni siquiera puedes saber cuánta memoria está usando tu aplicación, debido al intercambio de memoria y otras técnicas de optimización. Puede obtener lecturas de memoria utilizando los comandos de shell habituales, pero esas son estimaciones muy, muy aproximadas.
Timo Ohr
20

La respuesta de @ hp.android funciona bien si solo está trabajando con fondos de mapas de bits pero, en mi caso, tuve que BaseAdapterproporcionar un conjunto de ImageViews para a GridView. Modifiqué el unbindDrawables()método según lo recomendado para que la condición sea:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

pero el problema es que el método recursivo nunca procesa los hijos de AdapterView. Para abordar esto, en su lugar hice lo siguiente:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

para que los hijos del AdapterViewtodavía se procesen, el método simplemente no intenta eliminar todos los hijos (lo que no es compatible).

Sin embargo, esto no soluciona el problema, ya que ImageViewadministra un mapa de bits que no es su fondo. Por lo tanto, agregué lo siguiente. No es ideal pero funciona:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

En general, el unbindDrawables()método es entonces:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

Espero que haya un enfoque más basado en principios para liberar esos recursos.

darrenp
fuente
12

Buena charla de Google I / O (2011) sobre Gestión de memoria en Android, así como detalles sobre herramientas + técnicas para la creación de perfiles de memoria:
http://www.youtube.com/watch?v=_CruQY55HOk

greg7gkb
fuente
44
O la publicación de blog correspondiente: android-developers.blogspot.com/2011/03/…
greg7gkb
1
Bien, entonces tengo un usuario que cambia constantemente entre actividades, lo que significa que pueden haber cambiado 15 actividades en 20 segundos. ¿Podría ser eso la causa de un error de falta de memoria? ¿Qué debo hacer para arreglarlo? ¡Gracias!'
Ruchir Baronia
1

Bueno, esas son las herramientas que se enganchan con los formatos únicos que usa Android ... Creo que lo que puede no estar satisfecho es el marco de código de prueba subyacente en uso ...

¿Has intentado simular áreas de código de prueba con Android Mock Framework?

Fred Grott
fuente
1
no tanto, las pruebas de que la naturaleza no es tanto el tema como la grabación de lo que está sucediendo realmente mientras se ejecuta la aplicación, lo que realmente necesita es una pérdida de recursos / memoria de perfiles de herramientas
jottos