Sempre que o aplicativo cria uma nova linha de execução, o sistema operacional precisa alocar memória para ela. Embora as linhas de execução sejam geralmente consideradas "leves" em comparação com processos completos, o custo de memória delas não é zero. O vazamento de linhas de execução é uma maneira muito comum de vazar memória no Android.
O custo de uma linha de execução
Quando uma linha de execução é criada no Android, dois componentes principais consomem memória:
- Pilha nativa: a biblioteca
pthreadaloca uma pilha para a linha de execução armazenar variáveis locais e frames de chamada. No Android, o padrão é 1 MB. - Estruturas internas da JVM/do kernel: o ART e o kernel do Linux
precisam alocar estruturas de dados (como objetos
pthread_internal_tejava.lang.Thread) para rastrear e gerenciar a linha de execução.
Memória de pilha e mmap
É importante entender que o Android aloca a pilha nativa de 1 MB usando
mmap com as flags MAP_ANONYMOUS | MAP_NORESERVE.
Isso significa que o SO reserva 1 MB de espaço de endereço de memória virtual, mas não aloca 1 MB de RAM física imediatamente. A memória física só é alocada (paginada) quando a linha de execução realmente executa o código e envia dados para a pilha. Uma linha de execução que não faz nada consome pouca RAM física para a pilha.
Exercício prático: vazamento de linhas de execução
Vamos usar o aplicativo de exemplo MemoryLab para observar o que acontece quando um aplicativo cria centenas de linhas de execução inativas.
1. Lançamento e valor de referência
Inicie o app e faça uma leitura de memória de referência:
adb shell am start -W -n com.android.memorylab/.MainActivity
adb shell dumpsys meminfo -s com.android.memorylab
Observe as linhas Stack e Native Heap na saída. Elas provavelmente serão
pequenas.
2. Criar 100 conversas
Toque no botão Criar 100 conversas. O aplicativo vai gerar 100 novas linhas de execução. Para garantir que a memória da pilha seja realmente paginada na RAM física (para que possamos vê-la nas nossas ferramentas), cada linha de execução aloca e grava em uma pequena matriz de 10 KB na pilha antes de começar a esperar.
Execute o comando meminfo novamente enquanto as linhas de execução estiverem ativas:
adb shell dumpsys meminfo -s com.android.memorylab
Os resultados:
Compare a nova saída com o valor de referência. Você vai notar aumentos significativos enquanto as linhas de execução estiverem ativas:
Stack: aumentou em um megabyte ou mais. Isso representa as páginas físicas alocadas para as 100 pilhas de linhas de execução (cada uma tocando pelo menos 10 KB).Native Heap/Private Other: aumentou devido às estruturas internas depthreadalocadas pelo sistema para cada linha de execução.
Para limpar, toque no botão Destroy All Threads.
3. Como visualizar linhas de execução no Perfetto
O Perfetto é excelente para rastrear ciclos de vida e contagens de linhas de execução ao longo do tempo.

PerfettoSQL para análise de linhas de execução
É possível consultar o rastro para contar exatamente quantas linhas de execução foram geradas pelo aplicativo:
SELECT p.name AS process_name, COUNT(t.id) as thread_count
FROM thread t JOIN process p USING (upid)
WHERE p.name = 'com.android.memorylab' AND t.name LIKE 'LeakedThread-%';
Uma consulta como essa pode ser usada para gerar uma faixa de depuração com um contador, como mostrado na captura de tela.
← Código do app | ↑ Para cima | Localidade →