Un artículo para obtener "pérdida de memoria de Android"

Qué es una pérdida de memoria

En primer lugar, cuando creamos un objeto, por ejemplo, solicitamos un tipo List of String y lo declaramos como objeto A

Lista A = nueva ArrayList<>();

En este momento, el programa asignará la A delante del signo igual a la pila como referencia para nosotros.
La nueva ArrayList después del signo igual será asignada al montón por nosotros.

Sin embargo, por alguna razón, el programa no se libera o no se puede liberar, lo que resulta en una pérdida de memoria del sistema, provocando que el programa se congele o incluso que OOM se bloquee. Estas situaciones se denominan fugas de memoria.

Fugas de memoria comunes y cómo evitarlas

Algunos estudiantes pueden preguntar por qué la JVM tiene su propio mecanismo de GC y todavía pierde memoria.
Aunque existe un mecanismo de GC, algunos objetos no cumplen las condiciones de reciclaje de GC en circunstancias especiales (recuento de referencias, búsqueda de raíz)

①: fuerte referencia al objeto retenido.
②: Aunque este objeto ya no se usa, la referencia no se libera, lo que provoca que no se cumplan las condiciones de reciclaje del GC.
El resumen simple es: el ciclo de vida de referencia de un objeto excede el ciclo de vida que debería haber sobrevivido.

El modo singleton se refiere a la actividad

El modo singleton se refiere al contexto de la Actividad, y el ciclo de vida del singleton es el mismo que el de la aplicación después de la declaración.
Cuando se cierra la actividad, pero debido a que todavía se hace referencia al modo singleton, no se puede reciclar si no se satisface el GC.
Solución : use un contexto global, como application.getContext.

clase interna no estática

Las clases internas no estáticas (incluidas las clases internas anónimas) contienen referencias a clases externas de forma predeterminada y, cuando se hace referencia a clases internas no estáticas, la clase externa no se puede liberar.
Típico es el uso de Handler. Muchos desarrolladores implementarán rápidamente el uso de Handler de la siguiente manera:

public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start();
    }

    private void start() {
    
    
        Message msg = Message.obtain();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }

    private Handler mHandler = new Handler() {
    
    
        @Override
        public void handleMessage(Message msg) {
    
    
            if (msg.what == 1) {
    
    
                // 做相应逻辑
            }
        }
    };
}

Cuando la actividad finaliza, es posible que el mensaje aún exista en la cola de mensajes MessageQueue y no se procese o se esté procesando, lo que provocará que la actividad no se recicle, lo que provocará una pérdida de memoria de la actividad (recuperación rápida
) . Para declarar nuestro Handler

public class MainActivity extends AppCompatActivity {
    
    

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MyHandler(this);
        start();
    }

    private void start() {
    
    
        Message msg = Message.obtain();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }

    private static class MyHandler extends Handler {
    
    

        private WeakReference<MainActivity> activityWeakReference;

        public MyHandler(MainActivity activity) {
    
    
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
    
    
            MainActivity activity = activityWeakReference.get();
            if (activity != null) {
    
    
                if (msg.what == 1) {
    
    
                    // 做相应逻辑
                }
            }
        }
    }
}

De manera similar, existe otra situación en la que las clases internas provocan fugas de memoria, que es usar Thread o AsyncTask. Entonces las soluciones son las mismas, porque sus razones de generación son las mismas.

registrado anti-registro

Por ejemplo, si registramos una transmisión en la Actividad, si el registro no se cancela después de que se destruye la Actividad, entonces la transmisión siempre existirá en el sistema, manteniendo la referencia de Actividad al igual que la clase interna no estática mencionada anteriormente, lo que resulta en una pérdida de memoria. El mismo problema también existirá en algunos monitores personalizados que necesitan ser registrados y desregistrados.
Solución : Por lo tanto, después de registrar la transmisión y el monitoreo, el registro debe cancelarse después de que se destruya la Actividad.

Temporizador Temporizador


Timer y TimerTask generalmente se usan para realizar algunas tareas cíclicas o de tiempo en Android, como la solución ViewPager para un carrusel infinito : cuando se destruye nuestra actividad, Timer y TimerTask deben cancelarse de inmediato para evitar pérdidas de memoria.

Pérdida de memoria de WebView

Con respecto a la pérdida de memoria de WebView, porque WebView ocupará la memoria durante mucho tiempo después de cargar la página web y no se puede liberar, por lo que debemos llamar a su método destroy() para destruirlo después de que se destruya la Actividad para liberar la memoria.
Solución : antes de destruir WebView, debe eliminar WebView del contenedor principal y luego destruir WebView.

recurso no cerrado

Al usar IO, File stream o Sqlite, Cursor y otros recursos, debe cerrarse a tiempo. Estos recursos suelen utilizar buffers cuando realizan operaciones de lectura y escritura, si no se cierran a tiempo, estos objetos de buffer siempre estarán ocupados y no liberados, lo que provocará pérdidas de memoria.
Solución : Entonces los cerramos a tiempo cuando no necesitamos usarlos, para que el búfer pueda liberarse a tiempo para evitar pérdidas de memoria.

animacion de propiedades

La animación también es una tarea que requiere mucho tiempo. Por ejemplo, la animación de la propiedad (ObjectAnimator) se inicia en la Actividad, pero cuando se destruye, no se llama al método de cancelación. Aunque no podemos ver la animación, la animación continuará reproduciéndose. .Animación Hace referencia al control donde se encuentra, y el control donde se encuentra hace referencia a la Actividad, lo que provoca que la Actividad no se libere con normalidad.
Solución : Por lo tanto, también es necesario cancelar la animación del atributo cuando se destruye la actividad para evitar pérdidas de memoria.

Cómo localizar pérdidas de memoria

FugaCanario

Acceso:

Debido a que Leaks se instalará en nuestro terminal, solo necesitamos instalarlo durante la depuración, no durante el lanzamiento,
por lo que debemos agregar una línea en build.gradle del módulo usando debugImplementation

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'

Así es, solo necesita agregar esta línea y listo, otras operaciones de inicialización se realizarán automáticamente en la biblioteca LeakCanary.

usar:

Después de que se inicie LeakCanary, habrá una línea de inicio de sesión en Logcat

D LeakCanary: LeakCanary está funcionando y listo para detectar fugas

Esto significa que tenemos fugas en funcionamiento

Detección:

Solo necesitamos ingresar a algunas páginas a voluntad y luego salir, o si queremos verificar específicamente si hay una fuga en cierta actividad, podemos ingresar a esta actividad varias veces y luego salir. Después de un tiempo, aparecerá un brindis de este tipo. arriba. Significa que nuestra página tiene un problema de pérdida de memoria.
inserte la descripción de la imagen aquí
Si no hay un aviso, debemos tomar la iniciativa para abrir las fugas instaladas en el terminal,
ingresar a la entrada y hacer clic en el botón Volcar montón ahora, y
inserte la descripción de la imagen aquí
también podemos realizar un análisis de memoria de inmediato. En este momento, revisemos nuestro registro Nos
enfocamos principalmente en el campo Fuga.
inserte la descripción de la imagen aquí
Fuga tiene tres estados. , que representan diferentes significados
Fuga: SÍ: Se ha producido una fuga de memoria
Fuga: NO: No se ha producido ninguna fuga de memoria
Fuga: DESCONOCIDO: Desconocido, puede haber ocurrido una fuga
Aquí nuestro La captura de pantalla muestra que
se produjo una fuga de memoria en MainActivity2, la ubicación específica es MyHandler. A través de esta tabla de registro, podemos conocer claramente la situación y las incógnitas específicas de las fugas de memoria.
O habrá una salida de registro en nuestras fugas:
inserte la descripción de la imagen aquí
después de eso, puedo seguir las indicaciones para modificar nuestras fugas de memoria.

Perfil de Android

Detección de inicio:

Cuando nuestra información de LeakCanary no puede ayudarnos a localizar la ubicación de la fuga de memoria, pero nos recuerda que hay una fuga de memoria. Entonces podemos usar Android Proflier para ayudarnos a posicionarnos en posiciones relevantes.
inserte la descripción de la imagen aquí
1. Abra Profiler en la barra de herramientas de nuestro Android Studio
2. Haga clic en el signo más
3. Seleccione el proceso que queremos detectar
4. La luz verde indica el proceso que se está detectando

posición:

Haga doble clic en el área de la barra de memoria y aparecerá
inserte la descripción de la imagen aquí
la siguiente interfaz . En este momento, opere la posición o acción que desea detectar.
inserte la descripción de la imagen aquí
Es decir, el área que Leaks insinuó pero no puede encontrar.
Después de eso, haga clic con el botón derecho del mouse en "Forzar recolección de basura" muchas veces para recuperar manualmente la memoria y espere unos segundos.
inserte la descripción de la imagen aquí
Luego, haga clic con el botón derecho en el encabezado de volcado de Java. Si no hay nadie (versión superior), habrá un capture el volcado de pila a la izquierda y luego vaya al registro para
ingresar al análisis automático de pérdida de memoria Interfaz
Seleccione el nombre de nuestro paquete, seleccione una clase u objeto con una pérdida de memoria (marcado en un marco rojo),
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
"Esto es 1 leadk" con un signo de exclamación amarillo significa que hay una fuga de memoria.
Finalmente, analice el objeto de código fuente de la fuga de memoria. Después del cambio, siga los pasos anteriores y vuelva a intentarlo. Sí, hasta que no haya ninguna fuga.

ESTERA

Descargar MAT

enlace de descarga

Configurar el entorno MAT (mac)

Debido a que el formato de archivo hprof obtenido directamente del perfil de Android no es compatible con el formato mat, debe usar una herramienta para convertirlo.
1. Abra la terminal e ingrese:

echo $HOME

2. Continúe ingresando:

touch .bash_profile

3. Continúe ingresando:

open -e .bash_profile

4. Introduzca en el archivo bash abierto:

export PATH=${
    
    PATH}:/Users/用户名/你的sdk路径/platform-tools

5. Finalmente ingrese:

source .bash_profile

Use el perfil para obtener el archivo de análisis de memoria

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
Guardar como memoria-99.hprof

Abra la terminal para la conversión de archivos.

Formato de conversión: hprof-conv before.hprof after.hprof
Ingresamos aquí: hprof-conv memory-99.hprof 66.hprof puede entenderlo, convirtamos nuestro archivo fuente -99 en un archivo -66

Abre la herramienta mat e importa nuestro archivo -66

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
Haga clic en la opción en el cuadro rojo, esto es para el análisis de pérdida de memoria.
El siguiente es el objeto generado durante este período. Haga clic en el cuadro rojo para buscar directamente el objeto que desea analizar.
inserte la descripción de la imagen aquí
Aquí encontramos nuestra VideoPlayerActivity
y haga clic con el botón derecho
inserte la descripción de la imagen aquí
para verlo, lo que significa que excluimos las referencias blandas, débiles y fantasmas, porque estos tipos no causarán fugas de memoria y puede ignorarlas. Solo necesitamos ver si hay alguna referencia después de la exclusión. Si hay alguna, es una referencia fuerte, y sucederá Hay una pérdida de memoria.
¡Finalmente obtenga ese resultado
inserte la descripción de la imagen aquí
y luego combine el análisis de código y resuelva el problema! ! En cuanto a las muchas operaciones que se pueden hacer, debe averiguarlo usted mismo.

Supongo que te gusta

Origin blog.csdn.net/weixin_45112340/article/details/130506858
Recomendado
Clasificación