Compartir tecnología

Optimización del rendimiento de Android optimización de la memoria

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Optimización del rendimiento de Android optimización de la memoria

problema de memoria

  • paliza de memoria
  • pérdida de memoria
  • desbordamiento de memoria

paliza de memoria

La destrucción de memoria se refiere a la creación y destrucción de una gran cantidad de objetos en un corto período de tiempo, lo que resulta en actividades frecuentes de recolección de basura (Garbage Collection, GC). Esta actividad frecuente de GC consume muchos recursos de la CPU y puede provocar retrasos en las aplicaciones o degradación del rendimiento.

Rendimiento: la curva de memoria es irregular.

pérdida de memoria

Se produce una pérdida de memoria cuando una aplicación contiene referencias a objetos que ya no son necesarios, lo que hace que el recolector de basura no pueda recuperar estos objetos, ocupando así espacio de memoria que podría haberse liberado. Con el tiempo, las pérdidas de memoria dan como resultado cada vez menos memoria disponible, lo que eventualmente puede provocar fallas en las aplicaciones o degradación del rendimiento.

desbordamiento de memoria

El desbordamiento de memoria ocurre cuando una aplicación intenta asignar más espacio de memoria, pero el sistema no puede satisfacer la solicitud porque ya no hay suficiente espacio de memoria para asignar. Esto normalmente hace que la aplicación genere una excepción OutOfMemoryError.

Herramienta de detección

  • Perfilador de memoria
  • Analizador de memoria
  • FugaCanaria

Perfilador de memoria

  • Memory Profiler es la herramienta de análisis de memoria que viene con Android Studio.
  • Gráfico en tiempo real que muestra el uso de la memoria del programa.
  • Identifique pérdidas de memoria, palizas y más.
  • Proporciona la capacidad de capturar volcados de montón, forzar GC y realizar un seguimiento de las asignaciones de memoria.

Analizador de memoria

  • Potente herramienta de análisis de Java Heap para encontrar pérdidas de memoria y uso de memoria.
  • Genere informes generales, analice problemas y más.

FugaCanaria

  • Detección automática de pérdida de memoria.
    • LeakCanary detecta automáticamente fugas en estos objetos: Actividad, Fragmento, Vista, Modelo de vista, Servicio.
  • Sitio web oficial: https://github.com/square/leakcanary

Mecanismo de gestión de memoria.

Java

Estructura de la memoria Java: montón, pila de máquina virtual, área de método, contador de programa, pila de método local.

Algoritmo de reciclaje de memoria Java:

  • Algoritmo de marcar y barrer:
    1. Marcar objetos que necesitan ser reciclados.
    2. Recicle todos los objetos marcados de manera uniforme.
  • Algoritmo de copia:
    1. Divide el recuerdo en dos partes del mismo tamaño.
    2. Una vez que se agota un bloque de memoria, los objetos supervivientes se copian en otro bloque.
  • Algoritmo de clasificación y marcado:
    1. El proceso de marcado es el mismo que el algoritmo de "marcar y barrer".
    2. Los objetos de supervivencia se mueven hacia un extremo.
    3. Borre la memoria restante.
  • Algoritmo de recopilación generacional:
    • Combine las ventajas de múltiples algoritmos de recopilación.
    • La tasa de supervivencia de los objetos de nueva generación es baja y se utiliza un algoritmo de copia.
    • La tasa de supervivencia de los objetos de la generación anterior es alta y se utiliza el algoritmo de clasificación por marcas.

Desventajas del algoritmo de marcar y borrar: marcar y borrar no son eficientes y producirán una gran cantidad de fragmentos de memoria discontinuos.

Algoritmo de replicación: simple de implementar y eficiente de ejecutar. Desventajas: Se desperdicia la mitad del espacio.

Algoritmo de marca compacta: evita la fragmentación de la memoria causada por la limpieza de marca y evita desperdiciar espacio del algoritmo de copia.

Androidee

La asignación elástica de memoria de Android, el valor de asignación y el valor máximo se ven afectados por dispositivos específicos.

El algoritmo de reciclaje Dalvik y el algoritmo de reciclaje ART son mecanismos de recolección de basura utilizados para la administración de memoria en el sistema operativo Android.

Algoritmo de reciclaje de Dalvik:

  • Algoritmo de marca y barrido.
  • La ventaja es que es sencillo de implementar. La desventaja es que la ejecución de la aplicación se detendrá durante las fases de marcado y borrado, lo que provocará que la aplicación se congele temporalmente y afecte la experiencia del usuario.

Algoritmo de reciclaje de arte:

  • Algoritmo de recolección de basura por compactación. Se han realizado mejoras basadas en el algoritmo de barrido de marcas para reducir el tiempo de pausa durante la recolección de basura.
  • Marcado concurrente: ART introduce una fase de marcado concurrente, lo que significa que puede ocurrir al mismo tiempo que la ejecución de la aplicación. Esto reduce los tiempos de pausa debido a la recolección de basura.
  • Limpieza y compactación: durante la fase de limpieza, ART no solo borra los objetos no marcados, sino que también compacta la memoria, lo que significa que junta los objetos supervivientes para reducir la fragmentación de la memoria. Esto hace que la gestión de la memoria sea más eficiente y reduce la posibilidad de errores en la asignación de memoria.
  • Recolección adaptativa: ART también introduce el concepto de recolección adaptativa, lo que significa que ajusta automáticamente la frecuencia y la forma de recolección de basura en función del comportamiento de la aplicación y los patrones de uso de memoria. Esto permite que ART se adapte mejor a los diferentes requisitos de aplicación.

Mecanismo LMK:

  • Mecanismo Killer de baja memoria.
  • Su función principal es finalizar algunos procesos en segundo plano de acuerdo con una determinada política de prioridad cuando la memoria del sistema es insuficiente para liberar memoria y garantizar la estabilidad y capacidad de respuesta del sistema.

resolver

Problema de destrucción de memoria

Código de problema de simulación
public class ShakeActivity extends AppCompatActivity {

    private static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            String str = "";
            for (int i = 0; i < 10000000; i++) {
                str += i;
            }
            mHandler.sendEmptyMessageDelayed(1, 30);
        }
    };

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

    public void startClick(View view) {
        mHandler.sendEmptyMessage(1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
Utilice la herramienta Memory Profiler para detectar

Memory Profiler puede ver las asignaciones de memoria, haga clic en "Registrar asignaciones de Java/Kotlin".

Insertar descripción de la imagen aquí

Significado de lo anterior:

  • Java: Memoria asignada por código Java o Kotlin.
  • Nativo: Memoria asignada por código C o C++.
  • Gráficos: la memoria utilizada por la cola del búfer de gráficos para mostrar píxeles en la pantalla (incluidas superficies GL, texturas GL, etc.). Memoria compartida de la CPU.
  • Pila: la memoria utilizada por la pila nativa y la pila Java en la aplicación. Por lo general, esto tiene que ver con la cantidad de subprocesos que ejecuta su aplicación.
  • Memoria utilizada por las aplicaciones para procesar código y recursos como el código de bytes dex, el código dex optimizado o compilado, las bibliotecas .so y las fuentes.
  • La aplicación utiliza memoria que el sistema no está seguro de cómo clasificar.
  • La cantidad de objetos Java/Kotlin asignados por la aplicación. Este número no tiene en cuenta los objetos asignados en C o C++.

Los siguientes significados:

  • Asignaciones: Pasar malloc() onew El número de objetos asignados por el operador.
  • Desasignaciones: vía free() odelete El número de objetos desasignados por el operador.
  • Tamaño de asignaciones: el tamaño total de todas las asignaciones durante el período de tiempo seleccionado, en bytes.
  • Tamaño de desasignaciones: el tamaño total de la memoria liberada durante el período de tiempo seleccionado, en bytes.
  • Recuento total: Asignaciones menos desasignaciones.
  • Tamaño restante: tamaño de asignaciones menos tamaño de desasignaciones.
  • Tamaño superficial: el tamaño total de todas las instancias en el montón, en bytes.

Análisis de la imagen de arriba:

Insertar descripción de la imagen aquí

Los valores de asignaciones y desasignaciones en esta área son relativamente similares y el tamaño superficial es relativamente grande, lo que indica que los objetos se pueden crear y destruir con frecuencia.

Insertar descripción de la imagen aquí

Después de hacer clic, puede ver la información de la pila de llamadas y, combinada con el código, puede inferir que hay un problema de fluctuación de la memoria en el Controlador.

Consejos de optimización
  • Evite crear y destruir objetos con frecuencia.

Problema de pérdida de memoria

Código de problema de simulación
public class CallbackManager {
    public static ArrayList<Callback> sCallbacks = new ArrayList<>();

    public static void addCallback(Callback callback) {
        sCallbacks.add(callback);
    }

    public static void removeCallback(Callback callback) {
        sCallbacks.remove(callback);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class LeakActivity extends AppCompatActivity implements Callback {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak);
        ImageView imageView = findViewById(R.id.imageView);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
        imageView.setImageBitmap(bitmap);
        CallbackManager.addCallback(this);
    }

    @Override
    public void onOperate() {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
Utilice la herramienta LeakCanary para detectar

Agregar bibliotecas dependientes:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
  • 1

Después de que ocurre una pérdida de memoria, LeakCanary generará información relevante y la volcará automáticamente:

Insertar descripción de la imagen aquí

Como se puede ver en la figura anterior: LeakActivity tiene una pérdida de memoria y se muestra la relación de la cadena de referencia.

Por supuesto, también puede generar un archivo hprof y ver información específica a través de la herramienta Profiler:

Insertar descripción de la imagen aquí

Como se puede ver en la imagen de arriba: se han producido 10 puntos de fuga, incluido LeakActivity. Haga clic en LeakActivity para ver el objeto con pérdida de memoria y verifique la cadena de referencia.

Consejos de optimización
  • Reciclaje oportuno de elementos de recolección.
  • Evite referencias estáticas a demasiadas instancias.
  • Utilice clases internas estáticas.
  • Cierre los objetos de recursos rápidamente.

Optimización de mapa de bits

Si no libera los recursos de la imagen después de usar Bitmap, es fácil causarPérdida de memoria, lo que provoca un desbordamiento de memoria

Modelo de memoria de mapa de bits
  • Antes de api10 (Android 2.3.3): los objetos de mapa de bits se colocan en la memoria del montón y los datos de píxeles se colocan en la memoria local.
  • Después de api10: todo en la memoria del montón.
  • Después de api26 (Android8.0): los datos de píxeles se colocan en la memoria local. Esto permite que los datos de píxeles del mapa de bits de la capa nativa se liberen rápidamente junto con los objetos de la capa Java.

Reciclaje de memoria:

  • Antes de Android 3.0, debías llamarlo manualmenteBitmap.recycle()Realizar reciclaje de mapas de bits.
  • A partir de Android 3.0, el sistema proporciona una administración de memoria más inteligente y, en la mayoría de los casos, no es necesario reciclar manualmente Bitmap.

Configuración de píxeles de mapa de bits:

ConfiguraciónTamaño de bytes ocupados (byte)ilustrar
Alfa_81único canal transparente
RGB_5652Tinte RGB simple
ARGB_88884Color verdadero de 24 bits
RGBA_F168Android 8.0 Nuevo (HDR)

Calcule la memoria ocupada por Btimap:

  • Mapa de bits#getByteCount()
  • getWidth() * getHeight() * 1 píxel ocupa memoria
Directorio de archivos de recursos

Problema con el archivo de recursos:

  • mdpi (densidad media): aproximadamente 160 ppp, 1x recurso.
  • hdpi (alta densidad): aproximadamente 240 ppp, 1,5 veces los recursos.
  • xhdpi (densidad extra alta): aproximadamente 320 ppp, 2 veces los recursos.
  • xxhdpi (densidad extra ultraalta): aproximadamente 480 ppp, 3 veces los recursos.
  • xxxhdpi (densidad extra ultraalta): aproximadamente 640 ppp, 4 veces los recursos.

Código de prueba:

private void printBitmap(Bitmap bitmap, String drawable) {
    String builder = drawable +
            " Bitmap占用内存:" +
            bitmap.getByteCount() +
            " width:" +
            bitmap.getWidth() +
            " height:" +
            bitmap.getHeight() +
            " 1像素占用大小:" +
            getByteBy1px(bitmap.getConfig());
    Log.e("TAG", builder);
}

private int getByteBy1px(Bitmap.Config config) {
    if (Bitmap.Config.ALPHA_8.equals(config)) {
        return 1;
    } else if (Bitmap.Config.RGB_565.equals(config)) {
        return 2;
    } else if (Bitmap.Config.ARGB_8888.equals(config)) {
        return 4;
    }
    return 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
// 逻辑密度
float density = metrics.density;
// 物理密度
int densityDpi = metrics.densityDpi;
Log.e("TAG", density + "-" + densityDpi);

// 1倍图
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.splash1);
printBitmap(bitmap1, "drawable-mdpi");

// 2倍图
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.splash2);
printBitmap(bitmap2, "drawable-xhdpi");

// 3倍图
Bitmap bitmap3 = BitmapFactory.decodeResource(getResources(), R.drawable.splash3);
printBitmap(bitmap3, "drawable-xxhdpi");

// 4倍图
Bitmap bitmap4 = BitmapFactory.decodeResource(getResources(), R.drawable.splash4);
printBitmap(bitmap4, "drawable-xxxhdpi");

// drawable
Bitmap bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
printBitmap(bitmap5, "drawable");
/*
        3.0-480
        drawable-mdpi Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4
        drawable-xhdpi Bitmap占用内存:9281844 width:1287 height:1803 1像素占用大小:4
        drawable-xxhdpi Bitmap占用内存:4125264 width:858 height:1202 1像素占用大小:4
        drawable-xxxhdpi Bitmap占用内存:2323552 width:644 height:902 1像素占用大小:4
        drawable Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4
         */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

ilustrar:

1dp en dispositivos mdpi1px, 1dp en dispositivos xhdpi2px, 1dp==3px en dispositivo xxhdpi.

Por lo tanto, el dispositivo actual es xxhdpi, por lo que el ancho de la misma imagen bajo el recurso xxhdpi es 858, bajo el recurso mdpi se ampliará 3 veces y el ancho es 2574, y bajo el recurso xhdpi se ampliará 1,5 veces y el ancho es 1287.

Consejos de optimización
  • Configure múltiples conjuntos de recursos de imágenes.
  • Elija el método de decodificación apropiado.
  • Configurar caché de imágenes.

Descarga del código fuente