English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android Studio+Práctica de fugas de memoria en MATLAB

Para las fugas de memoria, en Android, si no se presta atención, es bastante fácil de producir, especialmente en Activity, es bastante común, a continuación, les hablaré de cómo busco fugas de memoria.

Primero, ¿qué es una fuga de memoria?

La fuga de memoria es algunos objetos que ya no se utilizan que aún existen en la memoria y el mecanismo de recolección de basura no puede reciclarlos, lo que los hace permanecer en la memoria, lo que hace que el consumo de memoria sea cada vez mayor, lo que eventualmente lleva a que el rendimiento del programa se degrade.
Dentro del motor de Android, se utiliza el algoritmo de búsqueda de nodos raíz para enumerar nodos raíz para determinar si es basura, el motor de virtualización comenzara a recorrer desde los GC Roots, si un nodo no puede encontrar una ruta que llegue a los GC Roots, es decir, no está conectado a los GC Roots, entonces se demuestra que la referencia es inválida y puede ser reciclada, la fuga de memoria es que algunas malas llamadas hacen que algunos objetos inútiles y GC Roots estén conectados, lo que los hace inaccesibles para la reciclaje.

Ya que sabemos qué es una fuga de memoria, naturalmente sabemos cómo evitarla, es decir, prestar atención a no mantener referencias largas a objetos inútiles al escribir código, aunque suene simple, se necesita suficiente experiencia para lograrlo, por lo que las fugas de memoria son bastante fáciles de producir, y aunque no se pueden evitar completamente, debemos poder encontrar y reparar las fugas de memoria que aparecen en el programa.
Ahora les voy a explicar cómo encontrar fugas de memoria.

Búsqueda de fugas de memoria:

Por ejemplo, el siguiente código:

public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    String string = new String();
  }
  public void click(View view){
    Intent intent = new Intent();
    intent.setClass(getApplicationContext(), SecondActivity.class);
    startActivity(intent);
  }
}
public class SecondActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(8000000L);
        catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    };
    new Thread(runnable).start();
  }
}

 

Cada vez que se saltara a esta Activity se invocara un hilo, luego ese hilo ejecutara el metodo run de runnable. Ya que Runnable es un objeto anonimo interno, posee una referencia a SecondActivity, por lo que dos Activity, pueden ser saltadas desde MainActivity a SecondActivity, a continuación, desde SecondActivity volvemos a MainActivity, y asi sucesivamente5vez, finalmente regresando a MainActivity. Según la lógica, después de regresar de SecondActivity a MainActivity, SecondActivity debería ser destruida y reciclada, pero en la práctica puede no ser así.

En este momento, para determinar si ha ocurrido un desbordamiento de memoria, es necesario usar herramientas. Hay dos formas

1utilizar la herramienta MAT para buscar

Primero, abra la herramienta Android Device Monitor en AS, como se muestra en la siguiente imagen:


Después de abrirlo, se mostrará la siguiente interfaz


Primero, seleccione el paquete de la aplicación que desea probar y luego haga clic en el lugar circunscrito en la imagen, se marcará un icono después del nombre del paquete del programa


Lo que sigue es operar nuestra aplicación para saltar de ida y vuelta5vez.

Luego haga clic en el icono de la siguiente imagen para exportar el archivo hprof para su análisis

El archivo exportado se muestra a continuación:

Después de obtener el archivo hprof, podemos analizarlo utilizando la herramienta MAT.

Abrir la herramienta MAT

Si no tiene, puede descargarlo en el siguiente sitio web

Dirección de descarga de la herramienta MAT

La interfaz se muestra a continuación:

Abrir el archivo hprof exportado anteriormente, y es probable que se informe del siguiente error

Esto se debe a que MAT se utiliza para analizar archivos hprof de programas java, y los archivos hprof exportados por Android tienen una cierta diferencia en el formato, por lo que necesitamos convertir los archivos hprof exportados, la herramienta de conversión proporcionada por sdk es hprof-conv.exe está en la ubicación de la siguiente imagen


A continuación, nos movemos a esta carpeta y ejecutamos el comando para convertir nuestro archivo hprof, como se muestra en la siguiente imagen


Donde hprof-Uso del comando conv, así:

hprof-conv archivo de origen archivo de salida

Por ejemplo, hprof-conv E:\aaa.hprof E:\output.hprof

Es decir, convertir aaa.hprof a output.hprof, el archivo output.hprof es el archivo que hemos convertido, como se muestra en la imagen mat2.hprof es el archivo que hemos convertido.

A continuación, usamos la herramienta MAT para abrir el archivo mat2archivo .hprof, luego abrirlo sin errores, como se muestra en la siguiente imagen:


Luego podemos ver los objetos que existen en la memoria actual, ya que las fugas de memoria generalmente ocurren en Activity, por lo que solo necesitamos buscar Activity.

Hacer clic en el icono QQL marcado en la siguiente imagen e ingresar select * from instanceof android.app.Activity

Similar a una sentencia SQL, encontrar información relacionada con Activity, hacer clic en el signo de exclamación rojo para ejecutar, como se muestra en la siguiente imagen:

接下来 我们就可以看到下面过滤到的Activity信息了

a continuación, podemos ver la información de Activity filtrada a continuación 6como se muestra en la imagen siguiente, aún existe

instancia de SecondActivity, pero queremos salir de todas ellas, lo que indica que ha habido una fuga de memoria

entre ellos, hay dos propiedades: Shallow size y Retained Size
Shallow Size
el tamaño de la memoria ocupada por el objeto en sí, sin incluir los objetos que él referencia. Para los objetos de tipo no array, su tamaño es la suma del tamaño del objeto y de todos sus variables miembro.
por supuesto, también incluirá algunos contenedores de datos de características del lenguaje Java. Para los objetos de tipo array, su tamaño es la suma del tamaño de los objetos de los elementos del array.
Retained Size+Retained Size=el tamaño del objeto actual-el tamaño total de los objetos a los que el objeto actual puede referenciar directamente o indirectamente. (El significado de la referencia indirecta: A->B
>C, C es la referencia indirecta)

sin embargo, al liberar, también hay que excluir los objetos que son referenciados directamente o indirectamente por GC Roots. No serán considerados como Garbage temporalmente.


a continuación, haga clic derecho en un SecondActivity

seleccionar with all references

abrir la página como se muestra a continuación

ver la página siguientever this0 referenciando estoAticitvythis0 representa el significado de clase interna, es decir, una clase interna hace referencia a Activity y this$0 es referenciado por target. target es un hilo, he encontrado la razón, la causa del derrame de memoria es que Activity es referenciado por la clase interna y la clase interna es utilizada por el hilo, por lo que no se puede liberar. Vamos a ver el código de esta clase.

public class SecondActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(8000000L);
        catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    };
    new Thread(runnable).start();
  }
}
De hecho, en

De hecho, en SecondActivity existe un objeto de clase interna Runnable, que luego es utilizado por el hilo, y el hilo debe ejecutar8000 segundos, por lo que el objeto SecondActivity está siendo referenciado y no puede ser liberado, lo que causó la sobrecarga de memoria.

Para resolver este tipo de sobrecarga de memoria, es necesario terminar la hilera a tiempo cuando la Activity sale (aunque no es muy bueno hacerlo), o controlar bien el tiempo de ejecución de la hilera.

De esta manera, hemos encontrado la sobrecarga de memoria en este programa.

2.Utilice directamente el Monitor Memory de Android Studio para encontrar la sobrecarga de memoria

Aún utilizando ese programa, lo explico de manera sencilla.

Primero, ejecute el programa en el teléfono, abra la interfaz de Monitor de AS para ver la imagen de Memory

Haga clic en el ícono del camión pequeño (en el gráfico1El ícono de ubicación) puede desencadenar una vez GC


Haga clic en2El ícono de ubicación puede ver el archivo hprof

A la izquierda están los objetos en la memoria, dentro de los cuales encontrar Activity para ver si existe la Activity que esperamos que ya se haya reciclado. Si aparece la Activity que esperamos que ya se haya reciclado, haga clic en ella para mostrar su número total en la derecha. Al hacer clic en alguna de las de la derecha, se puede mostrar el gráfico de la relación de GC Roots, y al ver el gráfico se puede encontrar la ubicación de la fuga de memoria (similar al primer método)

De esta manera, se ha completado la búsqueda de fugas de memoria.

Las causas de la fuga de memoria en Android se pueden dividir大致 en las siguientes几种 en general:

1.Fuga de memoria causada por la variable estática

Debido a que la vida útil de la variable estática comienza con la carga de la clase y termina con la descarga de la clase, es decir, la variable estática se libera cuando el proceso del programa muere, si se referencia la Activity en la variable estática, esta Activity, debido a que está siendo referenciada, tendrá la misma vida útil que la variable estática, y no podrá ser liberada, causando una fuga de memoria.

Solución:

Cuando la Activity es referenciada por una variable estática, utilice getApplicationContext porque el ciclo de vida de la Application es desde el inicio hasta el final del programa, y es similar a la variable estática.

2.Fuga de memoria causada por la hilera

En situaciones similares a los ejemplos anteriores, el tiempo de ejecución de la hilera es muy largo, incluso si la Activity sale a tiempo, aún se ejecutará, porque la hilera o el Runnable es una clase interna de Activity, por lo que posee una instancia de Activity (porque la creación de una clase interna depende de la clase externa), por lo que la Activity no puede ser liberada.

AsyncTask tiene una piscina de hilos, el problema es más grave.

Solución:

1Planificar razonablemente el tiempo de ejecución de los hilos, controlar que los hilos terminen antes que Activity.

2Cambiar la clase interna a la clase interna estática y usar la referencia débil WeakReference para guardar la instancia de Activity. Porque la referencia débil, una vez que el GC lo encuentre, lo reciclará, por lo que se puede reciclar rápidamente

3La ocupación de demasiado espacio de memoria de BitMap

El análisis de bitmap requiere ocupar memoria, pero la memoria solo proporciona8Asignar el espacio de M a BitMap, si hay demasiadas imágenes y no se recicla el bitmap a tiempo, se producirá un desbordamiento de memoria.

Solución:

Reciclar y comprimir las imágenes a tiempo antes de cargarlas

4La fuga de memoria causada por no cerrar los recursos a tiempo

Por ejemplo, algunos Cursor no se cierran a tiempo, lo que guarda una referencia a Activity, lo que conduce a fugas de memoria

Solución:

En el método onDestory, cerrarlo a tiempo

5La fuga de memoria causada por el uso de .Handler

Debido al uso de Handler, el handler enviará el objeto message al MessageQueue, luego Looper circulará en MessageQueue y extraerá Message para ejecutar. Pero si un Message no se ejecuta por mucho tiempo, debido a que Message tiene una referencia a Handler, y Handler generalmente también es un objeto de clase interna, Message tiene una referencia a Handler, Handler tiene una referencia a Activity, lo que hace que Activity no pueda ser reciclado.

Solución:

Siguiendo el uso de la clase interna estática+La forma de referencia débil puede resolver

Entre algunas de las cuestiones relacionadas con los objetos de conjunto no removidos, los objetos registrados no desregistrados y los problemas de presión de código, también pueden producir fugas de memoria. Pero generalmente, las soluciones mencionadas anteriormente suelen ser solucionables.

Te gustará