Participe do evento ⁠#Android11: apresentação de lançamento da versão Beta no dia 3 de junho.

Investigar o uso de RAM

No desenvolvimento de aplicativos Android, sempre preste atenção no quanto de memória de acesso aleatório (RAM) o seu aplicativo usa. Apesar de os tempos de execução da Dalvik e da ART realizarem coletas de lixo (GC) rotineiras, ainda é necessário entender quando e onde o aplicativo aloca e de onde libera memória. Para dar ao usuário uma experiência estável, em que o sistema operacional Android pode passar de um aplicativo a outro com muita velocidade, o seu aplicativo não pode consumir memória desnecessária quando o usuário não está interagindo com ele.

Mesmo que você siga todas as práticas recomendadas de gerenciamento da memória do aplicativo durante o desenvolvimento, ainda podem haver objetos que a sugam ou introduzem outros inconsistências de memória. A única forma de ter certeza de que o aplicativo esteja usando o mínimo de memória possível é analisando o uso de memória do aplicativo com as ferramentas abordadas aqui.

Interprete mensagens de registro

O lugar mais simples de se começar a investigar o uso de memória do aplicativo é nas mensagens de registro do tempo de execução. Às vezes, quando ocorre uma GC, você pode ver as mensagens no logcat.

Mensagens de registro da Dalvik

Na Dalvik (mas não na ART), toda GC grava as seguintes informações no logcat:

D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>

Exemplo:

D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms
Motivo da GC
O que acionou a GC e que tipo de coleta foi realizada. Os motivos possíveis são:
GC_CONCURRENT
Uma GC concomitante que libera memória à medida que a pilha começa a encher.
GC_FOR_MALLOC
Uma GC causada porque o aplicativo tentou alocar memória quando a pilha já estava cheia e, por isso, o sistema teve que parar o aplicativo e resgatar memória.
GC_HPROF_DUMP_HEAP
Uma GC que ocorre quando se solicita a criação de um arquivo HPROF para analisar a pilha.
GC_EXPLICIT
Uma GC explícita, como quando se chama gc() (que você deve evitar chamar, confiando que a GC será executada no momento certo).
GC_EXTERNAL_ALLOC
Isso acontece somente na API de nível 10 e em anteriores (as versões mais recentes alocam tudo na pilha da Dalvik). Uma GC da memória alocada externamente (como os dados de pixel armazenados em memória nativa ou em buffers de byte NIO).
Quantidade liberada
A quantidade de memória resgatada por essa GC.
Estatísticas da pilha
Percentual livre da pilha e (número de objetos ativos)/(tamanho total da pilha).
Estatística da memória externa
Memória alocada externamente em API de nível 10 ou anterior (quantidade de memória alocada)/(nível a partir do qual a coleta ocorre).
Tempo de pausa
Pilhas maiores terão tempo de pausa maior. Os tempos de pausa simultâneos mostram duas pausas: uma no início da coleta e outra perto do fim.

Enquanto essas mensagens de registro se acumulam, procure identificar aumentos na estatística da pilha (o valor 3571K/9991K no exemplo acima). Se esse valor continuar aumentando, é possível que haja um vazamento de memória.

Mensagens de registro da ART

Ao contrário da Dalvik, a ART não registra mensagens para GCs que não forem solicitadas explicitamente. As GCs só são gravadas quando são consideradas lentas. Em termos mais precisos, se a pausa da GC exceder 5 ms ou sua duração exceder 100 ms. Se o aplicativo não estiver em um estado de pausa perceptível, nenhuma das GCs será considerada lenta. GCs explícitas sempre são registradas.

A ART oferece as seguintes informações nas mensagens de registro das coletas de lixo:

I/art: <GC_Reason> <GC_Name> <Objects_freed>(<Size_freed>) AllocSpace Objects, <Large_objects_freed>(<Large_object_size_freed>) <Heap_stats> LOS objects, <Pause_time(s)>

Exemplo:

I/art : Explicit concurrent mark sweep GC freed 104710(7MB) AllocSpace objects, 21(416KB) LOS objects, 33% free, 25MB/38MB, paused 1.230ms total 67.216ms
Motivo da GC
O que acionou a GC e que tipo de coleta foi realizada. Os motivos possíveis são:
Concurrent
Uma GC simultânea que não suspende os encadeamentos do aplicativo. Essa GC é executada em um encadeamento do segundo plano e não impede alocações.
Alloc
GC iniciada porque o aplicativo tentou alocar memória quando a pilha já estava cheia. Nesse caso, a coleta de lixo ocorreu no encadeamento de alocação.
Explicit
A coleta de lixo foi solicitada explicitamente por um aplicativo, por exemplo, chamando gc() ou gc(). Assim como na Dalvik, a melhor maneira de abordar a coleta de lixo na ART é confiar na GC e evitar solicitar de GCs explícitas, se possível. GCs explícitas não são recomendadas porque bloqueiam o encadeamento de alocação e gastam ciclos de CPU desnecessariamente. As GCs explícitas também podem gerar instabilidade (repetições involuntárias, inconstância ou lentidão no aplicativo) se interromperem outros encadeamentos.
NativeAlloc
A coleta foi causada pela pressão da memória nativa por parte das alocações nativas, como os objetos Bitmaps ou RenderScript, que geram alocação.
CollectorTransition
A coleta foi causada por uma transição da pilha, que acontece quando se altera a GC em tempo de execução. As transições do coletor consistem em copiar todos os objetos de um espaço apoiado por lista livre para um espaço de ponteiro de incremento simples (ou vice-versa). Atualmente, as transições do coletor só ocorrerão quando um aplicativo alterar o estado dos processos de um estado de pausa perceptível para um estado de não pausa perceptível (ou vice-versa) em dispositivos de baixo nível de RAM.
HomogeneousSpaceCompact
A compactação homogênea de espaço é a compactação de espaço de lista livre com espaço de lista livre que normalmente ocorre quando um aplicativo é levado a um estado de pausa imperceptível do processo. Os principais motivos que levam a fazer isso são a redução do uso de RAM e a desfragmentação da pilha.
DisableMovingGc
Esse não é um motivo real de GC, mas sim uma observação de que a coleta foi bloqueada por conta do uso de GetPrimitiveArrayCritical enquanto a compactação da pilha simultânea estava em andamento. Em geral, o uso de GetPrimitiveArrayCritical não é recomendado de forma alguma em virtude das restrições aos coletores de movimento.
HeapTrim
Esse não é um motivo de GC, mas sim uma observação de que a coleta está bloqueada até a redução da pilha acabar.
Nome da GC
A ART tem diversas GCs que você pode executar.
Concurrent mark sweep (CMS)
Um coletor de toda a pilha que coleta livremente todos os espaços, menos o espaço de imagem.
Concurrent partial mark sweep
Um coletor de quase toda a pilha que coleta todos os espaços, menos os espaços de imagem e zigoto.
Concurrent sticky mark sweep
Um coletor geracional que só consegue liberar objetos alocados desde a última GC. Essa coleta de lixo é executada com mais frequência do que uma mark sweep completa ou parcial, já que é mais rápida e tem pausas mais curtas.
Marksweep + semispace
Uma GC de cópia não simultânea usada para transições da pilha e para compactação homogênea de espaços (para desfragmentar a pilha).
Objetos liberados
O número de objetos que foram resgatados por essa GC do espaço dos objetos não grandes.
Volume liberado
O número de bytes que foram resgatados por essa GC do espaço dos objetos não grandes.
Objetos grandes liberados
O número de objetos que foram resgatados por essa coleta de lixo do espaço dos objetos grandes.
Volume liberado dos objetos grandes
O número de bytes que foram resgatados por essa coleta de lixo do espaço dos objetos grandes.
Estatísticas da pilha
Percentual livre e (número de objetos ativos)/(tamanho total da pilha).
Tempos de pausa
Em geral, os tempos de pausa são proporcionais ao número de referências a objeto que foram modificadas durante a execução da GC. No momento, as GCs de CMS da ART só têm uma pausa, que é próxima ao fim da GC. As GCs de movimento têm uma pausa longa que representa a maior parte da duração da GC.

Se você vê muitas GCs no logcat, tente identificar aumentos na estatística da pilha (o valor 25MB/38MB no exemplo acima). Se esse valor continuar aumentando e nunca parecer ficar menor, é possível que haja um vazamento de memória. Uma alternativa é: se vir a GC que ocorreu pelo motivo “Alloc”, você já está operando perto da capacidade máxima da pilha e pode esperar a ocorrência de exceções de falta de memória no futuro.

Acesse o Android Monitor

  1. Abra o aplicativo em um dispositivo conectado ou emulador.
  2. Selecione View > Tool Windows > Android Monitor.
  3. No canto superior esquerdo do Android Monitor, selecione a aba Monitors.

    Figura 1. Android Monitor e três de seus monitores: Memory, CPU e GPU. No Android Studio, aumente o painel do Android Monitor no sentido vertical para ver o monitor Network.

Obtenha uma captura de pilha

A captura de pilha é um instantâneo de todos os objetos da pilha do aplicativo. A captura de pilha é armazenada em formato binário (HPROF), que você pode inserir em uma ferramenta de analítica, como o jhat. A captura da pilha do seu aplicativo contém informações sobre o estado geral da pilha do aplicativo para que você possa rastrear a origem de problemas que pode ter identificado ao visualizar atualizações da pilha.

  1. Na parte superior do monitor Memory, clique em Dump Java Heap .

    O Android Studio cria um arquivo de instantâneo da pilha com application-id_yyyy.mm.dd_hh.mm.hprof como o nome do arquivo, abre-o no Android Studio e adiciona-o à lista Heap Snapshot na aba Captures.

  2. Na aba Captures, clique com o botão direito do mouse no arquivo e selecione Export to standard .hprof.

Observação: Se você precisar ser mais preciso sobre o momento da criação da captura, poderá criar uma captura de pilha no ponto crítico do código do aplicativo chamando dumpHprofData().

Veja atualizações da pilha

Use o Android Monitor para ver atualizações da pilha do aplicativo em tempo real enquanto você interage com ele. As atualizações em tempo real fornecem informações sobre quanta memória é alocada para cada operação do aplicativo. Você pode usar essas informações para determinar se certa operação usa muita memória e precisa ser ajustada para usar menos.

  1. Interaja com o aplicativo e, no monitor Memory, veja a memória Free, ou livre, e Allocated, ou alocada.
  2. Clique em Dump Java Heap .
  3. Na aba Captures, clique duas vezes no arquivo de instantâneo da pilha para abrir o visualizador de HPROF.
  4. Para gerar a alocação da pilha, interaja com o aplicativo e clique em Initiate GC .

Continue interagindo com o aplicativo e inicie GCs. Veja a alocação da pilha ser atualizada a cada GC. Identifique que ações do aplicativo geram muita alocação e onde é possível reduzir alocações e liberar recursos.

Analise a captura de pilha

A captura de pilha é fornecida em um formato similar, mas não idêntico, ao da ferramenta HPROF Java. A principal diferença de uma captura de pilha do Android é que há um grande número de alocações no processo Zigoto. Como as alocações Zigoto são compartilhadas por todos os processos do aplicativo, elas não importam muito para a análise da sua pilha.

Para analisar a captura da pilha, você pode usar uma ferramenta padrão como o jhat. Para usar o jhat, você deve converter o arquivo do formato HPROF do Android para o formato HPROF do SE Java. Para converter para o formato HPROF do Java SE, use a ferramenta hprof-conv presente no diretório ANDROID_SDK/platform-tools/. Execute o comando hprof-conv com dois argumentos: o arquivo HPROF original e o local para gravar o arquivo HPROF convertido. Por exemplo:

hprof-conv heap-original.hprof heap-converted.hprof

é possível carregar o arquivo convertido em uma ferramenta de análise de pilha que entenda o formato HPROF do Java SE. Durante a análise, procure vazamentos de memória causados por um dos seguintes motivos:

  • Referências duradouras a Activity, Context, View, Drawable e outros objetos que podem conter uma referência ao contêiner Activity ou Context.
  • Classes internas não estáticas, como Runnable, que possam conter uma instância de Activity.
  • Caches que armazenar objetos por mais tempo do que o necessário.

Controle as alocações de memória

Controlar as alocações de memória pode dar a você uma maior compreensão de onde seus objetos sugadores de memória estão alocados. Você pode usar o Allocation Tracker para buscar usos de memória específicos e analisar caminhos críticos do código no aplicativo, como a rolagem.

Por exemplo, você pode usar o Allocation Tracker para controlar alocações enquanto rola uma lista no aplicativo. O controle permite ver todas as alocações de memória necessárias para rolar a lista, em que encadeamento as alocações de memória estão e de onde vieram. Esse tipo de informação pode ajudar a simplificar os caminhos de execução para reduzir o trabalho deles, o que melhora o funcionamento geral do aplicativo e da interface do usuário.

Embora não seja necessário nem mesmo possível remover todas as alocações de memória dos caminhos críticos de desempenho do código, o Allocation Tracker pode ajudar a identificar problemas importantes no código. Por exemplo, um aplicativo pode criar um novo objeto Paint a cada desenho. Tornar o objeto Paint global é uma mudança simples que ajuda a melhorar o desempenho.

  1. Abra o aplicativo em um dispositivo conectado ou emulador.
  2. No Android Studio, selecione View > Tool Windows > Android Monitor.
  3. No canto superior esquerdo do Android Monitor, selecione a aba Monitors.
  4. Na barra de ferramentas “Memory Monitor”, clique em “Allocation Tracker” para iniciar as alocações de memória.
  5. Interaja com o aplicativo.
  6. Clique em Allocation Tracker novamente para parar o controle de alocação.

    O Android Studio cria um arquivo de alocação com application-id_yyyy.mm.dd_hh.mm.alloc como o nome do arquivo, abre-o no Android Studio e adiciona-o à lista Allocations na aba Captures.

  7. No arquivo de alocação, identifique que ações do aplicativo provavelmente geram muita alocação e determine em que pontos ele deve tentar reduzir as alocações e liberar recursos.

Para obter mais informações sobre como usar o Allocation Tracker, consulte Allocation Tracker.

Veja um resumo das alocações de memória

Em uma análise mais detalhada, você pode querer observar como a memória do aplicativo é dividida entre diferentes tipos de alocação de RAM com o seguinte comando adb:

adb shell dumpsys meminfo <package_name|pid> [-d]

O sinalizador -d fornece mais informações relacionadas ao uso de memória da Dalvik e da ART.

A saída lista todas as alocações atuais do aplicativo, medidas em kilobytes.

Ao examinar essa informação, você deve conhecer bem os seguintes tipos de alocação:

RAM privada (limpa e suja)
Essa é a memória usada somente pelo seu processo. Essa é a quantidade de RAM que o sistema pode resgatar quando o processo do aplicativo é destruído. Geralmente, a parte mais importante é a RAM suja privada, que é a mais custosa porque só é usada pelo seu processo e seu conteúdo fica exclusivamente em RAM, então não pode ser paginado para armazenamento (porque o Android não usa “swap”). Todas as alocações de pilha nativas e da Dalvik que você fizer serão RAM suja privada. As alocações nativas e da Dalvik que você compartilhar com o processo Zigoto compartilham RAM suja.
Tamanho do conjunto proporcional (PSS)
Essa é uma medida do uso de RAM do seu aplicativo que leva em conta páginas compartilhadas entre processos. Toda página de RAM exclusiva do seu processo contribui diretamente para o valor de PSS, enquanto que as páginas compartilhadas com outros processos contribuem para o valor PSS somente na proporção da quantidade compartilhada. Por exemplo, uma página compartilhada entre dois processos contribuirá metade do seu tamanho para o PSS de cada processo.

Uma característica interessante da medida de PSS é que é possível adicionar o PSS a todos os processos para determinar a memória realmente sendo usada por todos eles. Isso significa que o PSS é uma boa medida para o peso real em RAM de um processo e para comparação com o uso de RAM de outros processos e com a RAM total disponível.

Por exemplo, veja abaixo a saída de um processo de mapa em um dispositivo Nexus 5. Há muitas informações aqui, mas os pontos principais foram listados abaixo.

adb shell dumpsys meminfo com.google.android.apps.maps -d

Observação: as informações abaixo podem diferir do que é mostrado aqui porque alguns detalhes da saída mudam de acordo com a versão da plataforma.

** MEMINFO in pid 18227 [com.google.android.apps.maps] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    10468    10408        0        0    20480    14462     6017
  Dalvik Heap    34340    33816        0        0    62436    53883     8553
 Dalvik Other      972      972        0        0
        Stack     1144     1144        0        0
      Gfx dev    35300    35300        0        0
    Other dev        5        0        4        0
     .so mmap     1943      504      188        0
    .apk mmap      598        0      136        0
    .ttf mmap      134        0       68        0
    .dex mmap     3908        0     3904        0
    .oat mmap     1344        0       56        0
    .art mmap     2037     1784       28        0
   Other mmap       30        4        0        0
   EGL mtrack    73072    73072        0        0
    GL mtrack    51044    51044        0        0
      Unknown      185      184        0        0
        TOTAL   216524   208232     4384        0    82916    68345    14570

 Dalvik Details
        .Heap     6568     6568        0        0
         .LOS    24771    24404        0        0
          .GC      500      500        0        0
    .JITCache      428      428        0        0
      .Zygote     1093      936        0        0
   .NonMoving     1908     1908        0        0
 .IndirectRef       44       44        0        0

 Objects
               Views:       90         ViewRootImpl:        1
         AppContexts:        4           Activities:        1
              Assets:        2        AssetManagers:        2
       Local Binders:       21        Proxy Binders:       28
       Parcel memory:       18         Parcel count:       74
    Death Recipients:        2      OpenSSL Sockets:        2

Veja um dumpsys mais antigo do aplicativo do Gmail na Dalvik:

** MEMINFO in pid 9953 [com.google.android.gm] **
                 Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
               Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
              ------  ------  ------  ------  ------  ------  ------  ------  ------
  Native Heap      0       0       0       0       0       0    7800    7637(6)  126
  Dalvik Heap   5110(3)    0    4136    4988(3)    0       0    9168    8958(6)  210
 Dalvik Other   2850       0    2684    2772       0       0
        Stack     36       0       8      36       0       0
       Cursor    136       0       0     136       0       0
       Ashmem     12       0      28       0       0       0
    Other dev    380       0      24     376       0       4
     .so mmap   5443(5) 1996    2584    2664(5) 5788    1996(5)
    .apk mmap    235      32       0       0    1252      32
    .ttf mmap     36      12       0       0      88      12
    .dex mmap   3019(5) 2148       0       0    8936    2148(5)
   Other mmap    107       0       8       8     324      68
      Unknown   6994(4)    0     252    6992(4)    0       0
        TOTAL  24358(1) 4188    9724   17972(2)16388    4260(2)16968   16595     336

 Objects
               Views:    426         ViewRootImpl:        3(8)
         AppContexts:      6(7)        Activities:        2(7)
              Assets:      2        AssetManagers:        2
       Local Binders:     64        Proxy Binders:       34
    Death Recipients:      0
     OpenSSL Sockets:      1

 SQL
         MEMORY_USED:   1739
  PAGECACHE_OVERFLOW:   1164          MALLOC_SIZE:       62

Em geral, atenha-se às colunas Pss Total e Private Dirty. Em alguns casos, as colunas Private Clean e Heap Alloc também oferecem dados interessantes. Se quiser mais informações sobre as diferentes alocações de memória (as linhas), preste atenção no seguinte:

Dalvik Heap
A RAM usada no seu aplicativo pelas alocações da Dalvik. O Pss Total inclui todas as alocações Zigoto (ponderadas com base no compartilhamento entre processos, como descrito na definição de PSS acima). O número de Private Dirty é a RAM real empregada somente à pilha do seu aplicativo, composto das suas alocações e de todas as páginas de alocação Zigoto que foram modificadas depois da bifurcação entre o processo do aplicativo e o Zigoto.

Observação: em versões de plataforma mais recentes que têm a seção Dalvik Other, os números de Pss Total e Private Dirty para a pilha da Dalvik não abrangem sobrecarga da Dalvik, como a compilação “just-in-time” (JIT) e o agendamento de GC, enquanto que as versões mais antigas listam ambos combinados em Dalvik.

O Heap Alloc é a quantidade de memória que as alocações de pilha da Dalvik e nativas controlam para o seu aplicativo. Esse valor é maior que Pss Total e Private Dirty porque o seu processo foi separado do Zigoto e por conter alocações que o seu processo compartilha com todos os outros.

.so mmap e .dex mmap
A RAM em uso para código .so (nativo) e .dex (Dalvik ou ART). O número de Pss Total inclui código da plataforma compartilhado entre aplicativos, já o de Private Clean é o código do seu próprio aplicativo. Geralmente, o tamanho mapeado real é muito maior — a RAM aqui é só o que é necessário estar em RAM no momento para o código executado pelo aplicativo. Porém, o mapa .so tem uma RAM suja privada maior devido às correções do código nativo quando ele foi carregado no endereço final.
.oat mmap
Quantidade de RAM usada pela imagem do código que é baseada nas classes pré-carregadas comumente usadas por diversos aplicativos. Essa imagem é compartilhada por todos os aplicativos e não é afetada por nenhum aplicativo em especial.
.art mmap
Quantidade de RAM usada pela imagem da pilha que se baseia nas classes pré-carregadas comumente usadas por diversos aplicativos. Essa imagem é compartilhada por todos os aplicativos e não é afetada por nenhum aplicativo em especial. Embora a imagem da ART contenha instâncias de Object, ela não é considerada para o tamanho da pilha.
.Heap (somente com sinalizador -d)
Quantidade de memória da pilha do seu aplicativo. Exclui objetos da imagem e espaços de objeto grande, mas inclui o espaço do zigoto e espaço fixo.
.LOS (somente com sinalizador -d)
Quantidade de RAM usada pelo espaço de objetos grandes da ART. Inclui objetos grandes do zigoto. Objetos grandes são alocações de matriz primitiva maiores do que 12 KB.
.GC (somente com sinalizador -d)
Quantidade de GC interna considerada como sobrecarga do aplicativo. Não há uma forma de reduzir essa sobrecarga.
.JITCache (somente com sinalizador -d)
Quantidade de memória usada pelos caches de código e dados JIT. Normalmente, esse valor é zero, já que todos os aplicativos serão compilados assim que instalados.
.Zygote (somente com sinalizador -d)
Quantidade de memória usada pelo espaço do zigoto. O espaço do zigoto é criado durante a inicialização do dispositivo e nunca há alocações dentro dele.
.NonMoving (somente com sinalizador -d)
Quantidade de RAM usada pelo espaço fixo da ART. O espaço fixo contém objetos não movíveis, como campos e métodos. É possível reduzir essa parte usando menos campos e métodos no aplicativo.
.IndirectRef (somente com sinalizador -d)
Quantidade de RAM usada pelas tabelas de referência indireta da ART. Normalmente, esse é um valor pequeno mas, se estiver muito alto, talvez seja possível reduzi-lo diminuindo o número de referências a JNI locais e globais usadas.
Unknown
Todas as páginas da RAM que o sistema não conseguiu classificar em dos outros itens mais específicos. Atualmente, aqui ficam muitas das alocações nativas que não podem ser identificadas pela ferramenta durante a coleta desses dados em virtude da Aleatorização do layout do espaço de endereço (ASLR, na sigla em inglês). Como na pilha da Dalvik, o Pss Total para “Unknown” leva em consideração o compartilhamento com o Zigoto e Private Dirty é a RAM desconhecida dedicada somente ao seu aplicativo.
TOTAL
O total de RAM do Tamanho do conjunto proporcional (PSS) usado pelo processo. Essa é a soma de todos os campos de PSS acima. Indica o peso geral do seu processo na memória, que pode ser comparado diretamente com outros processos e com a RAM total disponível.

Private Dirty E Private Clean são o total de alocações dentro do processo que não são compartilhadas com outros processos. Juntas (especialmente o Private Dirty), formam a quantidade de RAM que será devolvida ao sistema quando o processo for destruído. A RAM suja são páginas que foram modificadas e, portanto, continuam empregadas à RAM (porque não há “swap”). Já a RAM limpa são páginas que foram mapeadas de um arquivo persistente (como código em execução) e, por isso, podem ser “despaginadas” se ficarem ociosas por algum tempo.

ViewRootImpl
O número de vistas-raiz ativas no processo. Cada vista-raiz está associada a uma janela, por isso, fica mais fácil identificar vazamentos de memória relacionados a caixas de diálogo ou outras janelas.
AppContexts e Activities
O número de objetos Context e Activity do aplicativo que atualmente estão ativos no processo. Isso pode ajudar você a identificar rapidamente objetos Activity com vazamentos que não podem ser coletados por uma coleta de lixo por terem referências estáticas, o que é comum. Muitas vezes, esses objetos têm muitas outras alocações associadas a eles, o que os torna uma boa maneira de controlar grandes vazamentos de memória.

Observação: Um objeto View ou Drawable também contém uma referência à Activity de que se origina, portanto, manter um objeto View ou Drawable também pode gerar vazamentos em uma Activity do aplicativo.

Acione vazamentos de memória

Quando usar as ferramentas descritas acima, procure estressar agressivamente o código do aplicativo e tente forçar vazamentos de memória. Uma forma de provocar vazamentos de memória no aplicativo é deixá-lo em execução por um tempo antes de inspecionar a pilha. Os vazamentos se acumularão no topo das alocações na pilha. Porém, quanto menor o vazamento, maior o tempo necessário de execução do aplicativo para vê-lo.

Você também pode acionar um vazamento de memória de uma das seguintes maneiras:

  1. Girar o dispositivo da orientação de retrato para a de paisagem e vice-versa diversas vezes em diferentes estados de atividade. Girar o dispositivo pode fazer o aplicativo gerar um vazamento em um objeto Activity Context ou View porque o sistema recria a Activity e, se o aplicativo contiver uma referência a um desses objetos em algum lugar, o sistema não poderá removê-lo pela coleta de lixo.
  2. Alterne entre o seu aplicativo e outro com a atividade em diferentes estados (navegue para a tela inicial, depois volte para o aplicativo).

Dica: Você também pode realizar as etapas acima usando a estrutura de testes monkey. Para saber mais sobre como usar a estrutura de testes monkey, leia a documentação do monkeyrunner.