Vazamento de memória em aplicativos móveis — o que é, causas e métodos de detecção

Autor: IT Sectr Publicado: 2026-03-29 Tempo de leitura: 9 min

Um vazamento de memória (Memory Leak) é uma situação em que o aplicativo mantém referências a objetos que não são mais necessários, impedindo o coletor de lixo de liberar a memória ocupada. Segundo o LeakCanary, mesmo em aplicativos bem escritos ocorrem de 3 a 5 vazamentos a cada 10.000 linhas de código. Cada vazamento reduz gradualmente a memória disponível, levando a lentidão e OutOfMemoryError.

Principais pontos

  • Memory Leak — um objeto permanece na memória mesmo sem referências ativas a ele na lógica do aplicativo
  • Referências estáticas a Activity ou Context — a causa mais comum de vazamentos no Android
  • LeakCanary — a ferramenta padrão para detecção automática de vazamentos no Android
  • WeakReference e Application Context — técnicas básicas para prevenir vazamentos
  • Componentes Lifecycle-aware eliminam toda uma classe de vazamentos relacionados a assinaturas

O que é um vazamento de memória

Um vazamento de memória (Memory Leak) é uma situação em que um objeto permanece acessível através de uma cadeia de referências fortes (Strong Reference), embora logicamente não seja mais necessário para o aplicativo. O coletor de lixo (GC) considera tal objeto como vivo e não libera a memória que ele ocupa. Como resultado, a memória disponível do heap diminui constantemente e a frequência de pausas do GC aumenta.

Ao contrário de linguagens com gerenciamento manual de memória (C, C++), em Java/Kotlin um vazamento não é um free() esquecido, mas uma referência esquecida. Enquanto existir uma strong reference de uma GC Root até o objeto vazado, o GC o considera necessário. GC Roots típicas: campos estáticos, threads ativas, pilha de chamadas, referências globais JNI.

O perigo dos vazamentos é seu efeito cumulativo. Um vazamento de 100 KB é imperceptível, mas 100 desses vazamentos ocupam 10 MB, e o aplicativo começa a ficar lento devido a GCs frequentes. Uma massa crítica de vazamentos leva a OutOfMemoryError e à queda do aplicativo. Sintomas de um vazamento: crescimento constante do consumo de memória no gráfico do Profiler, pausas frequentes do GC com STW (Stop The World) e degradação do desempenho da UI.

Tipos comuns de vazamentos em aplicativos móveis

Cinco tipos de vazamentos cobrem 95% dos casos no desenvolvimento móvel. Cada um tem sua própria causa e padrão de código característico.

Referência estática a Activity ou Context

O mais conhecido vazamento no Android é armazenar uma referência estática a uma Activity ou Context. Código típico: um campo estático Activity que não é anulado em onDestroy(). Enquanto o campo estático viver, toda a Activity vive com sua árvore de View, que pode ocupar de 1 a 10 MB. Este é o vazamento clássico que o LeakCanary encontra primeiro.

Solução: nunca armazene Activity ou Context em campos estáticos. Use Application Context para singletons que sobrevivem à Activity. Se precisar de uma referência a uma Activity, use WeakReference<Activity>.

kotlin
object MySingleton {
    private var weakActivity: WeakReference<Activity>? = null

    fun attach(activity: Activity) {
        weakActivity = WeakReference(activity)
    }
}

Classes internas com referência implícita

Classes anônimas e classes internas não estáticas mantêm implicitamente uma referência à classe contêiner. Um Runnable passado para um Handler que executa após onDestroy() retém toda a Activity. Um callback do Retrofit que captura uma Activity faz o mesmo. Este é o tipo mais insidioso de vazamento — a referência implícita não é visível no código.

Expressões object e lambdas do Kotlin também capturam referências à classe externa. Torne as classes internas estáticas (ou de nível superior em Kotlin) e passe referências externas através de WeakReference. Para lambdas, use uma abordagem Lifecycle-aware com viewLifecycleOwner.

Listeners e assinaturas não cancelados

Assinar serviços do sistema sem cancelar a assinatura é um vazamento direto. SensorManager, LocationManager, NotificationListener registrados em onResume() sem chamar unregister em onPause() retêm a Activity. Da mesma forma: um Disposable do RxJava não adicionado a um CompositeDisposable e uma corrotina lançada através de GlobalScope.

Use componentes Lifecycle-aware: observe() com LifecycleOwner cancela a assinatura automaticamente em onDestroy(). Para RxJava — viewLifecycleOwner.lifecycle.addObserver com DisposableObserver. Para corrotinas — lifecycleScope.launch() está vinculado ao ciclo de vida.

kotlin
// cancelamento automático de assinatura via Lifecycle
viewModel.userData.observe(viewLifecycleOwner) { data ->
    updateUI(data)
}

// corrotinas com lifecycleScope
lifecycleScope.launch {
    viewModel.loadData().collect { render(it) }
}

Bitmap sem recycle

Um Bitmap ocupa uma quantidade significativa de memória do heap: um bitmap FullHD são 1920 × 1080 × 4 bytes = 8,3 MB. Se um Bitmap for criado para cada item da lista e recycle() não for chamado ao ocultar, a memória se esgota rapidamente. Em versões antigas do Android (anteriores a 3.0), o Bitmap era armazenado na memória nativa, mas nas modernas está no heap Dalvik/ART, e o GC só pode liberá-lo se não houver uma strong reference.

Use Glide ou Coil para carregar imagens — essas bibliotecas gerenciam cache e reciclagem automaticamente. Se trabalhar diretamente com Bitmap, chame bitmap.recycle() para imagens grandes que não são mais exibidas e use inSampleSize para carregar cópias reduzidas.

Referência a Fragment após onDestroyView

Um Fragment tem dois ciclos de vida: o do próprio Fragment e o de sua View. Após onDestroyView(), a árvore View é destruída, mas o próprio Fragment pode permanecer na memória se houver uma referência externa. Um erro típico é armazenar uma referência a um Fragment em um adaptador ViewPager ou em um gráfico de navegação que não é limpo ao ser destruído.

Nunca armazene uma referência a um Fragment nos campos de objetos de longa duração. Use childFragmentManager para fragments aninhados e observe() com LifecycleOwner para transferência de dados entre eles. O ViewPager2 resolveu esse problema no nível da API: FragmentTransactionAdapter gerencia corretamente o ciclo de vida.

Como detectar um vazamento de memória

A detecção de um vazamento requer verificar dois fatos: a memória não retorna após o tempo de vida esperado e a quantidade de objetos de um determinado tipo cresce sem diminuir. O processo de diagnóstico inclui três etapas.

A primeira etapa é uma verificação visual através do Memory Profiler no Android Studio. Abra a guia Memory, execute a ação desejada (abra e feche a tela), pressione GC (Garbage Collection) e veja se a memória retorna ao nível inicial. Se após 3–4 ciclos de abertura-fechamento a memória crescer consistentemente — há um vazamento.

A segunda etapa é fazer um Heap Dump. No Memory Profiler, pressione Dump Java Heap. Abra o arquivo .hprof resultante no Android Studio: você verá todos os objetos no heap com tamanhos e referências. Procure por classes cuja contagem deve ser zero após fechar a tela. Por exemplo, MainActivity com contagem 2 após o fechamento é um vazamento óbvio.

A terceira etapa é analisar o Retained Size e a GC Root. No Android Studio, analise o Retained Size: quanta memória será liberada se você remover este objeto. O caminho da GC Root até o objeto mostra o que o está segurando: Static field → HashMap → Activity — e você vê o ponto do vazamento. O painel Reference mostra todos os detentores do objeto.

Ferramentas para detecção de vazamentos

Quatro ferramentas cobrem a busca de vazamentos desde a detecção automática até a análise profunda de Heap Dump.

FerramentaMétodoFormato dos resultados
LeakCanaryMonitoramento automáticoHeap Dump + stack trace do vazamento
Android Memory ProfilerMonitoramento manualGráfico de memória + Heap Dump
MAT (Eclipse)Análise profundaRelatório Dominator Tree + caminho GC Root
PerfettoRastreamento de sistemaLinha do tempo + memória nativa

LeakCanary é indispensável para qualquer projeto Android. Ele detecta automaticamente vazamentos no final do ciclo de vida de Activity/Fragment e mostra o local exato do vazamento com um stack trace. Integração: uma linha no build.gradle. LeakCanary 2.x não requer inicialização manual — ele registra automaticamente o Application Watcher.

Como prevenir vazamentos de memória

A prevenção de vazamentos é incorporada ao processo de desenvolvimento através de um conjunto de regras e ferramentas que verificam o código em cada etapa.

Regra de referências fortes

Nunca armazene uma referência a uma Activity, Fragment ou View em um campo estático, singleton ou objeto de longa duração. Se a referência for inevitável, use WeakReference ou armazene dados através de ViewModel, que vive exatamente o tempo necessário e não retém uma View diretamente.

Arquitetura Lifecycle-aware

ViewModel e LiveData do Android Architecture Components resolvem o problema do ciclo de vida no nível da arquitetura. ViewModel sobrevive à rotação da tela e não contém referências a View. LiveData cancela automaticamente a assinatura do observer em onDestroy(). Use-os em vez de assinatura manual em serviços do sistema.

Code Review focado em GC Root

Durante a revisão de código, preste atenção a: campos estáticos com tipos Context/View, classes anônimas, lambdas capturando Activity, assinaturas manuais, RxJava disposable sem composite, armazenamento de Fragment via Bundle. Em Kotlin, verifique adicionalmente corrotinas com launch sem vinculação ao ciclo de vida.

Verificação automática em CI

LeakCanary pode funcionar como parte do pipeline de testes: execute testes de aceitação com LeakCanary e falhe a compilação se um vazamento for encontrado. Isso impede que vazamentos cheguem à produção. Complemente a verificação com a regra StaticFieldLeak do Android Lint — ela encontra vazamentos potenciais no nível de análise estática.

kotlin
// LeakCanary em testes
class LeakTest {
    @Test
    fun activityShouldNotLeak() {
        ActivityScenario.launch(MainActivity::class.java)
            .close()
        LeakAssertions.assertNoLeak() // falhar se houver um vazamento
    }
}

Perguntas frequentes

Qual a diferença entre vazamento de memória e OutOfMemoryError?

O vazamento é a causa, e OutOfMemoryError é a consequência. Um vazamento não leva ao OOM, mas o acúmulo de dezenas de vazamentos esgota o Heap. OOM é uma exceção fatal, enquanto o vazamento é um padrão que leva a ela com o tempo.

Como encontrar um vazamento sem LeakCanary?

Através do Android Memory Profiler: abra e feche a tela 5 vezes, após cada fechamento invoque o GC. Se a memória não voltar ao nível base — há um vazamento. Faça um Heap Dump e encontre na lista a classe Activity cuja contagem seja maior que 0 após o fechamento.

O Kotlin pode prevenir vazamentos no nível da linguagem?

Parcialmente. Kotlin resolve o problema de null-safety, mas não gerencia strong references. Corrotinas com lifecycleScope e viewModelScope previnem vazamentos de tarefas em segundo plano, enquanto sealed class e data class reduzem o número de estados que levam a vazamentos. A proteção principal são padrões arquiteturais, não características da linguagem.

Por que o LeakCanary encontra um vazamento que não existe?

LeakCanary às vezes dá falsos positivos: um objeto pode ser temporariamente retido pelo sistema (por exemplo, InputMethodManager retém a última View). Verifique manualmente: se o Retained Size for < 1 KB e a GC Root for um serviço do sistema, provavelmente é um falso positivo.

Vazamentos de memória só existem no Android?

Não. Vazamentos são possíveis em qualquer plataforma com GC: iOS (Swift/Objective-C), Flutter (Dart), navegadores web (JavaScript). Os mecanismos são os mesmos — strong reference de uma GC Root. No iOS, o ARC gerencia a memória automaticamente, mas retain cycles entre objetos criam o mesmo vazamento.

Resumo

  • Memory Leak — um objeto que o GC não pode liberar devido a uma strong reference esquecida
  • Referências estáticas a Activity e Context — a causa mais comum de vazamentos
  • Referências implícitas através de classes anônimas, lambdas e assinaturas RxJava são mais insidiosas que as explícitas
  • LeakCanary encontra vazamentos automaticamente e mostra o stack trace exato
  • Componentes Lifecycle-aware (ViewModel, LiveData, lifecycleScope) eliminam uma classe de vazamentos
  • Heap Dump e análise de Retained Size — o principal método de diagnóstico manual
  • Prevenção inclui code review focado em strong references e verificação em CI com LeakCanary

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também