Compartilhamento de tecnologia

Otimização de memória de otimização de desempenho do Android

2024-07-12

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

Otimização de memória de otimização de desempenho do Android

problema de memória

  • destruição de memória
  • vazamento de memória
  • estouro de memória

destruição de memória

A destruição da memória refere-se à criação e destruição de um grande número de objetos em um curto período de tempo, resultando em atividades frequentes de coleta de lixo (Garbage Collection, GC). Essa atividade frequente de GC consome muitos recursos da CPU e pode causar atrasos nos aplicativos ou degradação do desempenho.

Desempenho: A curva de memória está irregular.

vazamento de memória

Um vazamento de memória ocorre quando uma aplicação retém referências a objetos que não são mais necessários, fazendo com que esses objetos não sejam reciclados pelo coletor de lixo, ocupando assim espaço de memória que poderia ter sido liberado. Com o tempo, vazamentos de memória resultam em cada vez menos memória disponível, o que pode eventualmente levar a travamentos de aplicativos ou degradação de desempenho.

estouro de memória

O estouro de memória ocorre quando um aplicativo tenta alocar mais espaço de memória, mas o sistema não consegue atender à solicitação porque não há mais espaço de memória suficiente para alocar. Isso geralmente faz com que o aplicativo gere uma exceção OutOfMemoryError.

Ferramenta de detecção

  • Perfilador de Memória
  • Analisador de memória
  • VazamentoCanário

Perfilador de Memória

  • Memory Profiler é a ferramenta de análise de memória que acompanha o Android Studio.
  • Gráfico em tempo real mostrando o uso de memória do programa.
  • Identifique vazamentos de memória, thrashing e muito mais.
  • Fornece a capacidade de capturar dumps de heap, forçar GC e rastrear alocações de memória.

Analisador de memória

  • Poderosa ferramenta de análise Java Heap para encontrar vazamentos e uso de memória.
  • Gere relatórios gerais, analise problemas e muito mais.

VazamentoCanário

  • Detecção automática de vazamento de memória.
    • LeakCanary detecta automaticamente vazamentos nestes objetos: Activity, Fragment, View, ViewModel, Service.
  • Site oficial: https://github.com/square/leakcanary

Mecanismo de gerenciamento de memória

Java

Estrutura de memória Java: heap, pilha de máquina virtual, área de método, contador de programa, pilha de método local.

Algoritmo de reciclagem de memória Java:

  • Algoritmo de marcação e varredura:
    1. Marque objetos que precisam ser reciclados
    2. Recicle todos os objetos marcados uniformemente.
  • Algoritmo de cópia:
    1. Divida a memória em dois pedaços de tamanhos iguais.
    2. Depois que um bloco de memória se esgota, os objetos sobreviventes são copiados para outro bloco.
  • Algoritmo de agrupamento de marcação:
    1. O processo de marcação é igual ao algoritmo "marcar e varrer".
    2. Os objetos de sobrevivência se movem para uma extremidade.
    3. Limpe a memória restante.
  • Algoritmo de coleta geracional:
    • Combine as vantagens de vários algoritmos de coleta.
    • A taxa de sobrevivência de objetos de nova geração é baixa e um algoritmo de cópia é usado.
    • A taxa de sobrevivência dos objetos da geração antiga é alta e o algoritmo de classificação de marca é usado.

Desvantagens do algoritmo mark-clear: A marcação e a limpeza não são eficientes e produzirão um grande número de fragmentos de memória descontínuos.

Algoritmo de replicação: simples de implementar e eficiente de executar. Desvantagens: Metade do espaço desperdiçado.

Algoritmo Mark-compact: evita a fragmentação da memória causada pela limpeza de marca e evita o desperdício de espaço do algoritmo de cópia.

Andróide

A alocação elástica de memória do Android, o valor de alocação e o valor máximo são afetados por dispositivos específicos.

O algoritmo de reciclagem Dalvik e o algoritmo de reciclagem ART são mecanismos de coleta de lixo usados ​​para gerenciamento de memória no sistema operacional Android.

Algoritmo de reciclagem Dalvik:

  • Algoritmo de marcação e varredura.
  • A vantagem é que é simples de implementar. A desvantagem é que a execução do aplicativo será pausada durante as fases de marcação e limpeza, o que fará com que o aplicativo congele temporariamente e afetará a experiência do usuário.

Algoritmo de reciclagem de arte:

  • Algoritmo de Compactação de Coleta de Lixo. Melhorias foram feitas com base no algoritmo de varredura de marca para reduzir os tempos de pausa durante a coleta de lixo.
  • Marcação Simultânea: O ART introduz uma fase de marcação simultânea, o que significa que ela pode ocorrer simultaneamente à execução da aplicação. Isso reduz os tempos de pausa devido à coleta de lixo.
  • Limpeza e compactação: Durante a fase de limpeza, o ART não apenas limpa objetos não marcados, mas também compacta a memória, o que significa que ele move os objetos sobreviventes juntos para reduzir a fragmentação da memória. Isso torna o gerenciamento de memória mais eficiente e reduz a chance de falhas na alocação de memória.
  • Coleta adaptativa: o ART também introduz o conceito de coleta adaptativa, o que significa que ajusta automaticamente a frequência e a forma de coleta de lixo com base no comportamento do aplicativo e nos padrões de uso de memória. Isto permite que o ART se adapte melhor aos diferentes requisitos de aplicação.

Mecanismo LMK:

  • Mecanismo Killer com pouca memória.
  • Sua principal função é encerrar alguns processos em segundo plano de acordo com uma determinada política de prioridade quando a memória do sistema é insuficiente para liberar memória e garantir a estabilidade e capacidade de resposta do sistema.

resolver

Problema de destruição de memória

Código do problema de simulação
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
Use a ferramenta Memory Profiler para detectar

O Memory Profiler pode visualizar alocações de memória, clique em "Registrar alocações Java/Kotlin".

Insira a descrição da imagem aqui

Significado do acima:

  • Java: memória alocada por código Java ou Kotlin.
  • Nativo: Memória alocada por código C ou C++.
  • Gráficos: A memória usada pela fila de buffer gráfico para exibir pixels na tela (incluindo superfícies GL, texturas GL, etc.). Memória compartilhada da CPU.
  • Pilha: a memória usada pela pilha nativa e pela pilha Java no aplicativo. Isso geralmente tem a ver com quantos threads seu aplicativo está executando.
  • Memória usada por aplicativos para processar código e recursos como bytecode dex, código dex otimizado ou compilado, bibliotecas .so e fontes.
  • O aplicativo usa memória que o sistema não tem certeza de como classificar.
  • O número de objetos Java/Kotlin alocados pelo aplicativo. Este número não leva em consideração objetos alocados em C ou C++.

Os seguintes significados:

  • Alocações: Passe malloc() ounew O número de objetos alocados pelo operador.
  • Desalocações: via free() oudelete O número de objetos desalocados pelo operador.
  • Tamanho das Alocações: O tamanho total de todas as alocações durante o período selecionado, em bytes.
  • Tamanho das desalocações: O tamanho total da memória liberada dentro do período selecionado, em bytes.
  • Contagem Total: Alocações menos Desalocações.
  • Tamanho restante: Tamanho das alocações menos Tamanho das desalocações.
  • Tamanho raso: o tamanho total de todas as instâncias no heap, em bytes.

Análise da imagem acima:

Insira a descrição da imagem aqui

Os valores de Alocações e Desalocações nesta área são relativamente semelhantes, e o Tamanho Raso é relativamente grande, indicando que objetos podem ser criados e destruídos com frequência.

Insira a descrição da imagem aqui

Após clicar, você pode visualizar as informações da pilha de chamadas e, combinadas com o código, pode inferir que há um problema de instabilidade de memória no manipulador.

Dicas de otimização
  • Evite criar e destruir objetos com frequência.

Problema de vazamento de memória

Código do problema de simulação
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
Use a ferramenta LeakCanary para detectar

Adicione bibliotecas dependentes:

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

Após ocorrer um vazamento de memória, o LeakCanary irá gerar informações relevantes e despejá-las automaticamente:

Insira a descrição da imagem aqui

Como pode ser visto na figura acima: LeakActivity apresenta vazamento de memória e o relacionamento da cadeia de referência é exibido.

Claro, você também pode gerar um arquivo hprof e visualizar informações específicas por meio da ferramenta Profiler:

Insira a descrição da imagem aqui

Como pode ser visto na imagem acima: ocorreram 10 pontos de vazamento, incluindo LeakActivity. Clique em LeakActivity para visualizar o objeto com vazamento de memória e verificar a cadeia de referência.

Dicas de otimização
  • Reciclagem oportuna de elementos de coleção.
  • Evite referências estáticas a muitas instâncias.
  • Use classes internas estáticas.
  • Feche os objetos de recursos imediatamente.

Otimização de bitmap

Se você não liberar os recursos da imagem após usar o Bitmap, é fácil causarVazamento de memória, resultando em estouro de memória

Modelo de memória bitmap
  • Antes da API10 (Android 2.3.3): os objetos bitmap são colocados na memória heap e os dados de pixel são colocados na memória local.
  • Após api10: tudo na memória heap.
  • Após api26 (Android8.0): os dados do pixel são colocados na memória local. Isso permite que os dados de pixel Bitmap da camada nativa sejam rapidamente liberados junto com os objetos da camada Java.

Reciclagem de memória:

  • Antes do Android 3.0, você precisa chamá-lo manualmenteBitmap.recycle()Execute a reciclagem de bitmap.
  • A partir do Android 3.0, o sistema oferece gerenciamento de memória mais inteligente e, na maioria dos casos, não há necessidade de reciclar manualmente o Bitmap.

Configuração de pixels de bitmap:

ConfiguraçãoTamanho do byte ocupado (byte)ilustrar
ALFA_81único canal transparente
RGB_5652Matiz RGB simples
ARGB_88884Cor verdadeira de 24 bits
RGBA_F168Android 8.0 novo (HDR)

Calcule a memória ocupada pelo Btimap:

  • Bitmap#obterContagemDeBytes()
  • getWidth() * getHeight() * 1 pixel ocupa memória
Diretório de arquivo de recursos

Problema no arquivo de recursos:

  • mdpi (densidade média): aproximadamente 160dpi, recurso 1x.
  • hdpi (alta densidade): aproximadamente 240 dpi, recursos 1,5x.
  • xhdpi (Extra High Density): Aproximadamente 320dpi, 2x recursos.
  • xxhdpi (Extra Ultra High Density): Aproximadamente 480dpi, 3x recursos.
  • xxxhdpi (Extra Ultra High Density): Aproximadamente 640dpi, 4x recursos.

Código de teste:

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 em dispositivos mdpi1px, 1dp em dispositivos xhdpi2px, 1dp==3px no dispositivo xxhdpi.

Portanto, o dispositivo atual é xxhdpi, então a largura da mesma imagem no recurso xxhdpi é 858, no recurso mdpi ela será ampliada 3 vezes e a largura é 2574, e no recurso xhdpi será ampliada 1,5 vezes e a largura é 1287.

Dicas de otimização
  • Configure vários conjuntos de recursos de imagem.
  • Escolha o método de decodificação apropriado.
  • Configure o cache de imagens.

Baixar código-fonte