Grandes requisitos de memória no Android - o que fazer?

Olá queridos leitores.

Hoje, chamamos a atenção para um pouco de material sobre o uso competente da memória no Android .



Gostar de ler!

Este artigo se concentra nas técnicas básicas para gerenciar o uso de memória em aplicativos - por exemplo, navegadores, editores de fotos e visualizadores de PDF - nos quais solicitações de memória grandes são feitas.

Primeiro, um pouco de teoria


A maioria dos aplicativos Android roda em cima do tempo de execução ( ART ), que substituiu a agora obsoleta máquina virtual Dalvik. ART e Dalvik são semelhantes à Java Virtual Machine (JVM), com a qual compartilham princípios de design semelhantes. Eles usam dois espaços separados para armazenar dados do aplicativo: a pilha e a pilha.

Memória de

pilha A memória de pilha em Java é usada para armazenar variáveis ​​locais (tipos primitivos e referências a objetos). Cada encadeamento Java possui sua própria pilha separada. A memória da pilha é relativamente pequena em comparação com a memória da pilha. O tamanho da pilha Java da Dalvik geralmente é de 32 KB para código Java e 1 MB para código nativo (C ++ / JNI). Uma pilha unificada para Java e C ++ apareceu no ART, cujo tamanho é de aproximadamente 1 MB.

Quando o aplicativo seleciona toda a memória da pilha até o limite, um erro é gerado StackOverflowError. Os motivos mais prováveis ​​de alcançar um limite de pilha são recursão infinita ou uma chamada de método muito profunda. As referências à memória da pilha são sempre feitas na ordem LIFO (último a chegar - primeiro a ser servido). Cada vez que um método é chamado, um novo quadro é empurrado para a pilha com as variáveis ​​locais desse método. Quando o método é concluído, seu quadro é retirado da pilha e qualquer valor resultante possível é enviado de volta à pilha. Portanto, o primeiro problema (recursão infinita) é um bug fácil de corrigir, mas o segundo exige alguma refatoração, que consiste em implantar chamadas de método recursivas e convertê-las em um loop.

Memória de pilha

A memória heap em Java é usada por uma máquina virtual para alocar objetos. Sempre que um objeto é criado, isso acontece no heap. Máquinas virtuais, como a JVM ou ART, coletam regularmente o lixo, removem todos os objetos que não são mais referenciados e, portanto, liberam memória para alocar novos objetos.
Para garantir a usabilidade, o Android limita fortemente os tamanhos de heap para cada aplicativo em execução. O limite de tamanho da pilha varia de dispositivo para dispositivo e depende da quantidade de RAM existente nesse dispositivo. Se seu aplicativo atingir o tamanho máximo de heap e tentar alocar mais memória, um erro será gerado OutOfMemoryErrore o aplicativo será encerrado. Vejamos alguns exemplos para ajudar a evitar essa situação.

Análise de memória heap


A ferramenta mais importante para entender problemas de memória em seus aplicativos e entender como a memória é usada é o criador de perfil de memória disponível no Android Studio.

Essa ferramenta visualiza a quantidade de memória que seu aplicativo consome ao longo do tempo. Você pode tirar instantâneos do heap Java em um aplicativo em execução, registrar as operações de alocação de memória e acompanhar o heap ou esse histórico de alocação de memória em uma interface do usuário poderosa.

Uma sessão típica de criação de perfil de memória deve ter esta aparência:

  • Examinamos as alocações de memória e passagens do coletor de lixo mais frequentes para identificar possíveis problemas de desempenho.
  • , , , , , . , . , , PdfActivity PSPDFKit .
  • , . , . – , , .


Os modernos coletores de lixo são obras complexas de arte tecnológica, resultado de muitos anos de pesquisa e desenvolvimento, nos quais participaram centenas de pessoas, de acadêmicos a desenvolvedores profissionais. No entanto, ainda é preciso estar alerta para evitar vazamentos de memória.

Uma solução exemplar para detectar vazamentos de memória é a biblioteca LeakCanary . Ele emite notificações automaticamente quando está na sua montagem de teste (desenvolvimento de desenvolvimento), fornecendo a taxa de rastreamento de vazamentos na interface do usuário deste programa. Você pode (e deve) integrá- lo hoje, principalmente porque não é difícil!

É especialmente fácil provocar vazamentos de memória ao trabalhar com ciclos de vida complexos de atividades ou fragmentos do Android. Isso geralmente acontece em pontos em que os desenvolvedores mantêm referências fortes a contextos da interface do usuário ou outros objetos específicos da interface do usuário na tarefa em segundo plano ou em variáveis ​​estáticas. Uma maneira de provocar tais atrasos é torcer ativamente o dispositivo ao testar seu aplicativo.

Libere memória em resposta a eventos


O Android pode exigir que o aplicativo aloque memória, ou simplesmente force-o a terminar quando a memória precisar ser liberada para executar tarefas mais críticas. Antes que isso aconteça, o sistema permitirá fornecer toda a memória que você não precisa. Em sua atividade, você precisará implementar uma interface ComponentCallbacks2. Nesse caso, sempre que o sistema ficar sem memória, será feita uma chamada ao seu método onTrimMemory()e você poderá liberar memória ou desativar recursos que não funcionarão sob essas condições de falta de memória.

Portanto, esses retornos de chamada são tratados no aplicativo PSPDFKit. O aplicativo PSPDFKit foi projetado com o cálculo do uso ativo da memória para armazenamento em cache, para que o aplicativo funcione da maneira mais suave possível. Inicialmente, não se sabe quanta memória está disponível no dispositivo; portanto, o PSPDFKit se adapta à situação e limita o uso de memória quando recebe notificações de que não há memória suficiente. Portanto, os aplicativos integrados ao PSPDFKit funcionam mesmo em dispositivos de baixa tecnologia, mas com desempenho reduzido devido ao fato de o cache estar desativado.

Pilha grande


Uma das soluções front-end para lidar com altos requisitos de memória é solicitar um grande número de Dalvik para seu aplicativo. Para fazer isso, você pode adicionar android:largeHeap="true"à marca <application> no arquivo AndroidManifest.xml.

Se a propriedade largeHeapestiver configurada como valor true, o Android criará todos os processos para seu aplicativo com uma grande pilha. Essa configuração destina-se apenas aos aplicativos que, por sua natureza, não podem funcionar sem ela, ou seja, eles usam recursos volumosos que devem caber simultaneamente na memória.

É altamente desencorajado usar uma pilha grande se você quiser apenas elevar o limite máximo para possível uso de memória. O uso da memória sempre deve ser otimizado, porque mesmo uma grande pilha do seu aplicativo pode não ser suficiente ao trabalhar em um dispositivo fraco com pouca memória.

Verifique quanta memória seu aplicativo pode usar


Nunca é demais verificar o tamanho da pilha do seu aplicativo e adaptar dinamicamente seu código e recursos disponíveis a esses limites de memória. Você pode verificar o tamanho máximo da pilha diretamente no tempo de execução usando métodos getMemoryClass()ou getLargeMemoryClass()(quando uma pilha grande está ativada).

O Android suporta até dispositivos com apenas 512 MB de RAM. Certifique-se de não ignorar os dispositivos de baixa tecnologia! Usando o métodoisLowRamDevice()Você pode verificar se o aplicativo está sendo executado em um dispositivo em que não há memória disponível suficiente. O comportamento exato desse método depende do dispositivo, mas geralmente retorna verdadeiro em dispositivos com menos de 1 GB de RAM. Você precisa garantir que seu aplicativo funcione corretamente nesses dispositivos e desativar todos os recursos que usam uma grande quantidade de memória neles.

Leia mais sobre como o Android funciona em dispositivos com uma pequena quantidade de memória, você pode ler aqui ; Dicas adicionais de otimização também são fornecidas aqui.

Use estruturas de dados otimizadas


Em muitos casos, os aplicativos usam muita memória pelo simples motivo de não usarem as estruturas de dados mais apropriadas.

As coleções Java não podem armazenar tipos primitivos eficientes e requerem compactar suas chaves e valores. Por exemplo, HashMapcom chaves inteiras devem ser substituídas por chaves otimizadas SparseArray. Por fim, você sempre pode usar matrizes brutas em vez de coleções, e é uma ótima idéia se sua coleção não for redimensionável.

Outras estruturas de dados que são ineficientes em termos de uso de memória incluem várias serializações. Sim, de fato, os formatos XML ou JSON são convenientes de usar; você pode reduzir o uso de memória se trabalhar com um formato binário mais eficiente, por exemplo, buffers de protocolo.

Todos esses exemplos, com estruturas de dados otimizadas para economizar memória, são apenas sugestões. Assim como na refatoração, você deve primeiro encontrar a fonte dos problemas e depois seguir para essas otimizações de desempenho.

Evitar embaralhar a memória


As máquinas virtuais Java / Android alocam objetos muito rapidamente. A coleta de lixo também é muito rápida. No entanto, ao alocar um grande número de objetos em um curto período de tempo, você pode encontrar um problema chamado “churn de memória”. Nesse caso, a máquina virtual não terá tempo para alocar objetos nesse ritmo, e o coletor de lixo os descartará, e o aplicativo começará a ficar mais lento e, em casos extremos, usará toda a memória.

O principal problema no território Android, neste caso, é que não controlamos quando a coleta de lixo ocorrerá. Potencialmente, isso pode causar problemas: por exemplo, o coletor de lixo funciona exatamente no momento em que a animação se desenrola na tela e excedemos o limite de 16 ms, o que garante uma exibição suave dos quadros. Portanto, é importante evitar a superalocação de memória no código.

Um exemplo de uma situação que leva ao embaralhamento de memória é a alocação de objetos grandes, por exemplo, Paint dentro do método de onDraw()apresentação. Nesse caso, muitos objetos são criados rapidamente e a coleta de lixo pode começar, o que pode afetar adversamente o desempenho dessa exibição. Como mencionado acima, você sempre deve monitorar o uso da memória para evitar tais situações.

Conclusão


A memória de acesso aleatório (RAM) em dispositivos móveis pode ser um recurso muito limitado. Garantir o uso eficiente da memória no aplicativo é especialmente importante se o aplicativo funcionar com objetos relativamente grandes, por exemplo, gráficos de varredura (visualizadores de PDF, navegadores da web, editores de fotos) ou arquivos de mídia grandes (editores de áudio ou vídeo). Seguindo essas dicas, você aprenderá como criar aplicativos de alta qualidade que funcionem em um nível aceitável, mesmo nos dispositivos mais avançados.

All Articles