Mi aplicación muestra un AlertDialog con un ListView en el interior. Todo funcionaba bien bun entonces decidí probar esto de pérdidas de memoria. Después de ejecutar la aplicación por algún tiempo abrí MAT y genera Fuga de los Sospechosos informe. MAT encontrado varios similares fugas:

Una instancia de «com.android.interna.app.AlertController$RecycleListView» cargado por «<sistema de cargador de clases>» ocupa …

Me pasé un montón de tiempo buscando la razón de esta pérdida. Revisión de código no me ayuda a mí y empecé a googlear. Eso es lo que he encontrado:

Tema 5054: AlertDialog parece provocar una pérdida de memoria a través de un Mensaje en el MessageQueue

Me decidí a comprobar si este error se reproduce o no. Para esto he creado un pequeño programa que consta de dos actividades. MainActivity es un enrty punto. Contiene sólo un botones que se ejecuta LeakedActivity. En el segundo, solo se muestra un AlertDialog en su onCreate() método. Aquí está el código:

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        findViewById(R.id.button).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(
                    new Intent(MainActivity.this, LeakedActivity.class));
            }
        });
    }
}

public class LeakedActivity extends Activity {
    private static final int DIALOG_LEAK = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
            showDialog(DIALOG_LEAK);
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        if (id == DIALOG_LEAK) {
            return new AlertDialog.Builder(this)
                .setTitle("Title")
                .setItems(new CharSequence[] { "1", "2" },
                    new OnClickListener() {
                        private final byte[] junk = new byte[10*1024*1024];

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //nothing
                        }
                    })
                .create();
        }
        return super.onCreateDialog(id);
    }
}

MAT informes de esta aplicación fugas com.android.internal.app.AlertController$RecycleListView cada vez que el AlertDialog es despedido y el LeakedActivity está terminado.

No soy capaz de encontrar cualquier error en este pequeño programa. Parece un caso muy simple de usar AlertDialog y debe funcionar bien, pero parece que no. Así que me gustaría saber cómo evitar fugas de memoria cuando se utiliza AlertDialogs con los elementos. Y ¿por qué no tiene este problema se ha solucionado todavía? Gracias de antemano.

OriginalEl autor Michael | 2011-08-16

4 Comentarios

  1. 27

    (2/12/2012): ver ACTUALIZACIÓN al final.

    Este problema no es en realidad causada por la AlertDialog pero más relacionadas con la ListView. Usted puede reproducir el mismo problema mediante la siguiente actividad:

    public class LeakedListActivity extends ListActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        //Use an existing ListAdapter that will map an array
        //of strings to TextViews
        setListAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, mStrings));
        getListView().setOnItemClickListener(new OnItemClickListener() {
            private final byte[] junk = new byte[10*1024*1024];
            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                    long arg3) {
            }
        });     
    }
        private String[] mStrings = new String[] {"1", "2"};
    }

    Girar el dispositivo varias veces, y obtendrá OOM.

    No tengo el tiempo para investigar más sobre lo que es la verdadera causa (sé que lo sucediendo, pero no está claro por qué está sucediendo; puede ser bug, o diseñado). Pero aquí tenemos un solución que usted puede hacer, al menos para evitar el OOM en su caso.

    Primer lugar, usted necesita para mantener una referencia a su filtró AlertDialog. Usted puede hacer esto en el onCreateDialog(). Cuando usted está utilizando setItems(), el AlertDialog se crea internamente un ListView. Y cuando se establece la onClickListener() en su setItems() llamada, internamente, se asignará a la ListView onItemClickListener().

    A continuación, en el que se filtró de la actividad onDestroy(), establecer el AlertDialog‘s ListView‘s onItemClickListener() a null, que dará a conocer la referencia a la escucha de un hacer cualquier memoria asignada dentro de ese oyente para ser elegible para la GC. De esta manera, usted no obtendrá OOM. Es sólo un solución y la solución real debería realmente estar incorporados en la ListView.

    Aquí se muestra un ejemplo de código para su onDestroy():

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(leakedDialog != null) {
                ListView lv = leakedDialog.getListView();
                if(lv != null)  lv.setOnItemClickListener(null);
        }
    }

    ACTUALIZACIÓN (2/12/2012): Después de más investigación, este problema no es realmente especial los relacionados con la ListView ni a OnItemClickListener, sino el hecho de que la GC no sucede de inmediato y la necesidad de tiempo para decidir qué objetos son elegibles y listo para el GC. Intente esto:

    public class MainActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            //this will create reference from button to 
            //the listener which in turn will create the "junk"
            findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
                private byte[] junk = new byte[10*1024*1024];
                @Override
                public void onClick(View v) {
                    //do nothing
                }
            });
        }
    }

    Girar un par de veces, y obtendrás el OOM. El problema es que después de girar, el junk todavía se conserva, ya que la GC no ha y no puede suceder sin embargo, si el uso de la ESTERA, verás que este junk todavía se mantiene el botón de escucha en lo profundo de la GCroot, y que tomará tiempo para que la GC para decidir si esta junk es elegible y puede ser GCed.) Pero, al mismo tiempo, un nuevo junk necesita ser creado después de la rotación, y debido a la mem alloc tamaño (10M por basura), esto hará que OOM.

    La solución es romper cualquier referencia a la persona que escucha (el junkpropietario), en este caso desde el botón, que prácticamente hace que el oyente como un GCroot con sólo camino corto a la basura y hacer que la GC decidir más rápido para recuperar la basura de la memoria. Esto se puede hacer en el onDestroy():

    @Override
    protected void onDestroy() {
        //this will break the reference from the button
        //to the listener (the "junk" owner)
        findViewById(R.id.button).setOnClickListener(null);
        super.onDestroy();
    }
    Genial!!! Estás en lo correcto. Y su solución funciona.
    Gran. ¿Te importaría marcado su pregunta como respondida en tanto que otras personas con problemas similares puede encontrar fácilmente la solución.
    Sí, por supuesto. Mi punto es que hay un montón de preguntas con respuestas buenas en stackoverflow, pero no están marcadas respuesta (la respuesta no fue aceptada). Va a ser bueno para la comunidad si todo el que pide, ser un poco responsable y marca/aceptar la respuesta, por lo que será más útil para los demás, con un problema similar.
    Su respuesta es la mejor por lo que la recompensa es el tuyo.
    Hola , en mi situación , no se trata de SetOnClickListiner , pero acerca de las Vistas y de la Imagen de los recursos utilizados en cada ListViewItem , ¿Cómo puedo solucionar este problema ?

    OriginalEl autor Ricky Lee

  2. 1

    No se puede reproducir para Android 2.3.4 2.3.3. He Probado el código exacto en un dispositivo real y por lo que veo en el LogCat, el tamaño de la pila se mantiene constante a lo largo del tiempo. Lamentablemente yo no era capaz de hprof-conf mi vertederos (ERROR: se esperaba 1.0.3)

    08-19 08:41:58.026: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 13K, 50% free 2698K/5379K, external 83K/519K, paused 16ms
    08-19 08:41:58.056: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1K, 43% free 3720K/6471K, external 83K/519K, paused 18ms
    08-19 08:45:30.113: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1076K, 58% free 2723K/6471K, external 595K/1042K, paused 18ms
    08-19 08:45:30.143: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3740K/6471K, external 507K/1019K, paused 19ms
    08-19 08:45:35.869: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1087K, 58% free 2726K/6471K, external 595K/1019K, paused 18ms
    08-19 08:45:35.899: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3744K/6471K, external 552K/1019K, paused 18ms
    08-19 08:45:39.112: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2734K/6471K, external 595K/1019K, paused 17ms
    08-19 08:45:39.152: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3752K/6471K, external 530K/1019K, paused 20ms
    08-19 08:46:14.186: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1089K, 58% free 2736K/6471K, external 595K/1019K, paused 23ms
    08-19 08:46:14.216: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 42% free 3755K/6471K, external 552K/1019K, paused 21ms
    08-19 08:46:16.519: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1092K, 58% free 2736K/6471K, external 595K/1019K, paused 23ms
    08-19 08:46:16.549: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3753K/6471K, external 530K/1019K, paused 22ms
    08-19 08:47:15.686: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1089K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
    08-19 08:47:15.716: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 3K, 42% free 3756K/6471K, external 561K/1019K, paused 18ms
    08-19 08:48:01.391: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2736K/6471K, external 595K/1019K, paused 19ms
    08-19 08:48:01.421: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3753K/6471K, external 530K/1019K, paused 19ms
    08-19 08:48:09.409: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1089K, 58% free 2737K/6471K, external 758K/1019K, paused 18ms
    08-19 08:48:09.449: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 42% free 3756K/6471K, external 561K/1019K, paused 21ms
    08-19 08:48:11.771: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1076K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
    08-19 08:48:11.811: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 530K/1019K, paused 20ms
    08-19 08:48:13.653: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1089K, 58% free 2737K/6471K, external 595K/1019K, paused 18ms
    08-19 08:48:13.683: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 3K, 42% free 3758K/6471K, external 561K/1019K, paused 19ms
    08-19 08:48:15.785: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
    08-19 08:48:15.825: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 530K/1019K, paused 19ms
    08-19 08:48:18.227: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1088K, 58% free 2737K/6471K, external 595K/1019K, paused 19ms
    08-19 08:48:18.257: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 42% free 3756K/6471K, external 552K/1019K, paused 20ms
    08-19 08:49:06.575: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1075K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
    08-19 08:49:06.605: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 530K/1019K, paused 17ms
    08-19 08:49:09.668: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1097K, 58% free 2729K/6471K, external 595K/1019K, paused 18ms
    08-19 08:49:09.708: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 43% free 3748K/6471K, external 552K/1019K, paused 20ms
    08-19 08:49:12.440: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
    08-19 08:49:12.470: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 43% free 3753K/6471K, external 530K/1019K, paused 17ms
    08-19 08:49:15.473: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1088K, 58% free 2736K/6471K, external 595K/1019K, paused 18ms
    08-19 08:49:15.503: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 4K, 42% free 3756K/6471K, external 561K/1019K, paused 17ms
    08-19 08:49:18.476: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1091K, 58% free 2737K/6471K, external 595K/1019K, paused 18ms
    08-19 08:49:18.506: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 507K/1019K, paused 20ms
    08-19 08:49:21.289: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1090K, 58% free 2737K/6471K, external 595K/1019K, paused 18ms
    08-19 08:49:21.319: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3754K/6471K, external 484K/996K, paused 20ms
    08-19 08:51:43.307: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1071K, 58% free 2723K/6471K, external 595K/996K, paused 17ms
    08-19 08:51:43.338: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed <1K, 43% free 3747K/6471K, external 595K/996K, paused 20ms
    08-19 08:51:45.620: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1086K, 58% free 2729K/6471K, external 595K/974K, paused 18ms
    08-19 08:51:45.660: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 7K, 43% free 3745K/6471K, external 462K/974K, paused 20ms
    08-19 08:51:47.421: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1080K, 58% free 2738K/6471K, external 595K/974K, paused 17ms
    08-19 08:51:47.452: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3755K/6471K, external 484K/974K, paused 19ms
    08-19 08:52:56.949: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 27K, 58% free 2733K/6471K, external 83K/595K, paused 18ms
    08-19 08:52:56.979: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed <1K, 42% free 3757K/6471K, external 83K/595K, paused 17ms
    08-19 08:53:01.233: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1072K, 58% free 2727K/6471K, external 595K/1107K, paused 18ms
    08-19 08:53:01.274: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 2K, 43% free 3749K/6471K, external 578K/1090K, paused 20ms
    08-19 08:53:04.046: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1081K, 58% free 2740K/6471K, external 595K/1064K, paused 18ms
    08-19 08:53:04.086: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3758K/6471K, external 530K/1042K, paused 20ms
    08-19 08:53:25.948: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1090K, 58% free 2740K/6471K, external 595K/1042K, paused 19ms
    08-19 08:53:25.978: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3758K/6471K, external 552K/1042K, paused 19ms
    08-19 08:57:51.246: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1099K, 58% free 2753K/6471K, external 595K/1042K, paused 18ms
    08-19 08:57:51.286: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 13K, 42% free 3764K/6471K, external 561K/1042K, paused 20ms
    08-19 09:00:47.699: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1106K, 58% free 2731K/6471K, external 595K/1019K, paused 18ms
    08-19 09:00:47.729: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 7K, 43% free 3748K/6471K, external 484K/996K, paused 17ms
    08-19 09:00:56.817: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1080K, 58% free 2741K/6471K, external 595K/996K, paused 18ms
    08-19 09:00:56.848: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3758K/6471K, external 530K/996K, paused 23ms
    08-19 09:01:00.701: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2739K/6471K, external 595K/996K, paused 19ms
    08-19 09:01:00.731: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3756K/6471K, external 530K/996K, paused 22ms
    08-19 09:01:25.916: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 1077K, 58% free 2739K/6471K, external 595K/996K, paused 18ms
    08-19 09:01:25.946: DEBUG/dalvikvm(3510): GC_FOR_MALLOC freed 6K, 42% free 3756K/6471K, external 530K/996K, paused 20ms
    Gracias por tu respuesta. Qué dispositivo lo has probado?
    Sólo he probado en Google Nexus S con Android 2.3.4 y fugas. Eso es bastante fácil asegurar si la fuga que existe. Inicializar junk con new byte[10*1204*1024], ejecutar LeakActivity y rotar un dispositivo varias veces. La aplicación se bloquea con OOM en Nexus S después de la segunda rotación.
    HTC Sensation w/ Revolución HD 1.1.8.
    He comprobado el código con el aumento de la basura de tamaño (5 MBytes). No hay ningún problema. Max montón (en mi caso) es reportado como 32 MByes (ActivityManager.getMemoryClass()). Tenga en cuenta que la GC por lo general se necesita algún tiempo para recuperar la memoria. Lo que no sé es, si el bloque de memoria tiene que ser en un solo fragmento, es decir, sin fragmentar.
    Hecho: 8 * 1024 * 1024. Cambio de orientación de 30 veces sin ningún problema. La memoria es rápidamente recuperada. Incluso con 2 o 3 buffers con acumulados de tamaño < 11 Mb funciona bien. Usted también podría estar interesado en ‘Google I/O 2011: gestión de la Memoria para las Aplicaciones de Android’ enlace

    OriginalEl autor Horst Dehmer

  3. 0

    Me había ejecutar el código y cuando yo presione primero el botón que está mostrando el LeakedActivity con diálogo y onClick es la eliminación de diálogo, pero la actividad sigue en primer plano con la pantalla en negro. Al pulsar de nuevo la tecla y, a continuación, de nuevo a partir de la actividad es mostrar la memoria de error de excepción:

    ERROR/AndroidRuntime(263): java.lang.OutOfMemoryError

    Luego me quitó la línea de private final byte[] junk = new byte[10*1024*1024]; en el cuadro de diálogo de código después de que no existe el problema….no sé por qué si alguien puede poner esta cosa en palabras, gracias a él/ella.

    I tested the code in emulator android 2.1
    Sí, te puedo decir por qué. Eso es debido a la pérdida de memoria pregunté. El junk campo fue añadido a llenar el montón más rápido. Debe ser GC ed después de la LeakedActivity está terminado, pero no lo es. Y ese es el punto.

    OriginalEl autor Vineet Shukla

  4. -2

    Que necesita para descartar/cancelar el cuadro de diálogo desde el método onClick como se muestra en el ejemplo aquí : Alerta de Diálogos en Android

    ¿Has probado esto? Como se puede ver este cuadro de diálogo es administrado por la actividad y automáticamente se cierra cuando la actividad es destruido.

    OriginalEl autor Brandon Haugen

Dejar respuesta

Please enter your comment!
Please enter your name here