LeakCanary é uma biblioteca de código aberto da Square para detecção automática de vazamentos de memória em aplicativos Android. Ela se integra ao processo de desenvolvimento e monitora em tempo real o ciclo de vida de Activity, Fragment, ViewModel e outros componentes, sinalizando vazamentos assim que ocorrem. De acordo com o Square Open Source, a biblioteca é usada em milhares de projetos e é considerada o padrão de fato para diagnóstico de memória no Android.
Principais pontos
LeakCanary é uma biblioteca para detecção automática de vazamentos de memória em aplicativos Android, desenvolvida pela Square. Ela se integra ao processo de compilação do aplicativo e monitora automaticamente se objetos que deveriam ser destruídos (Activity, Fragment, View) permanecem na memória. Ao detectar um vazamento, o LeakCanary gera um heap dump e analisa a cadeia de referências que retém o objeto.
A biblioteca se tornou um padrão na comunidade Android: de acordo com o GitHub, o projeto tem mais de 28 mil estrelas e é usado em aplicativos do Google, Uber, Airbnb e Facebook. O LeakCanary está disponível em duas versões principais: a clássica 1.x (com configuração manual) e a moderna 2.x (integração automática via ContentProvider). A versão 2.x não requer modificação da classe Application — a dependência é suficiente para o funcionamento completo.
A principal tarefa do LeakCanary é detectar quando um objeto continua existindo na memória após seu ciclo de vida ter terminado. Isso é típico de vazamentos através de campos estáticos, singletons, callbacks não registrados, classes anônimas e closures que capturam objetos externos.
Os vazamentos de memória no Android são mais críticos do que no desktop devido à RAM limitada em dispositivos móveis. Mesmo um vazamento de 5–10 MB em cada transição de tela pode levar a um OutOfMemoryError após 30–40 minutos de uso do aplicativo. O LeakCanary detecta esses problemas na fase de desenvolvimento, sem esperar por uma falha em produção.
LeakCanary usa referências fracas (WeakReference) combinadas com coleta de lixo forçada. Quando uma Activity ou Fragment chama onDestroy, o LeakCanary cria uma WeakReference para esse objeto e executa o GC após um breve atraso (5 segundos por padrão). Se o objeto ainda estiver acessível via WeakReference após o GC, ele está sendo retido por uma referência forte — um vazamento é registrado.
Após detectar um vazamento, o LeakCanary faz um heap dump (despejo de memória) — um instantâneo completo da memória do aplicativo no formato HPROF. Em seguida, o analisador integrado (Shark para a versão 2.x) constrói um grafo de acessibilidade das GC Roots até o objeto vazado e encontra o caminho mais curto — a cadeia de referências que mantém o objeto na memória.
// Lógica simplificada de detecção do LeakCanary
class ObjectWatcher {
private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()
fun watch(watchedObject: Any, description: String) {
val reference = KeyedWeakReference(watchedObject, description)
watchedReferences.add(reference)
BackgroundHandler.postDelayed({
checkForLeaks()
}, 5000)
}
private fun checkForLeaks() {
GcTrigger.runGc() // GC forçado
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // objeto sobreviveu ao GC — é um vazamento
}
}
}
}
O ponto chave é a chamada forçada a GcTrigger.runGc(). Sem ela, é impossível distinguir um objeto que realmente vazou de um que o GC ainda não coletou. O LeakCanary faz isso até três vezes: se após três ciclos de GC o objeto ainda estiver na memória, o vazamento é confirmado.
Shark é o analisador de heap dump integrado ao LeakCanary 2.x, escrito em Kotlin. Ao contrário do analisador anterior HAHA, o Shark não carrega todo o arquivo HPROF na memória, mas percorre seu grafo de objetos com alocações mínimas. Isso reduz o consumo de RAM durante a análise de 50 MB para 2–5 MB e encurta o tempo de análise de 30 segundos para 1–3 segundos.
Instalar o LeakCanary 2.x em um projeto Android moderno leva uma linha no build.gradle. A biblioteca usa ContentProvider para inicialização automática — não é necessário modificar a classe Application ou adicionar código ao MainActivity. A dependência é adicionada apenas para builds debug, para que os APKs de lançamento não contenham código extra.
// build.gradle (app/module)
dependencies {
// debugImplementation — biblioteca apenas para builds debug
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
Após adicionar a dependência e reconstruir o projeto, o LeakCanary aparece automaticamente no aplicativo. Na primeira inicialização, a biblioteca mostra uma notificação do sistema confirmando a ativação. Todos os vazamentos detectados aparecem como notificações — tocar em uma notificação abre uma tela com um relatório detalhado (LeakTrace).
Para personalização, você pode criar seu próprio AppWatcherInstaller e sobrescrever parâmetros: tempo limite do GC, lista de tipos de objetos rastreados, ativação do salvamento de heap dump em disco. No entanto, para 90% dos projetos, a configuração padrão é ideal.
A partir da versão 2.12, o LeakCanary suporta rastreamento automático de ViewModel, escopos de corrotinas e objetos State do Compose. Nenhuma dependência adicional é necessária — a biblioteca detecta automaticamente quais componentes do Jetpack estão sendo usados no projeto e ativa os detectores correspondentes.
Um relatório do LeakCanary (LeakTrace) é uma cadeia de referências de várias linhas da GC Root até o objeto vazado. Cada linha mostra a classe e o campo através dos quais uma referência forte passa. O desenvolvedor deve ler a cadeia de baixo para cima: a linha inferior é o objeto vazado, a linha superior é o ponto de entrada (GC Root).
Um LeakTrace típico se parece com: GC Root → campo estático da Application → singleton → callback → Activity. Se um desenvolvedor vê essa cadeia, o problema está claro: o singleton mantém um callback que capturou uma referência à Activity. A solução é substituir a referência forte por uma fraca no singleton.
┬
├─ android.app.Application
│ Leaking: NO (Application — singleton)
│ ↓ Application.leakedActivities
├─ java.util.ArrayList
│ Leaking: NO (ArrayList — normal)
│ ↓ ArrayList[0]
├─ com.example.MainActivity
│ Leaking: YES (Activity destroyed but still in memory)
│ ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│ Leaking: UNKNOWN
│ ↓ CallbackWrapper.mListener
│ ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│ Leaking: UNKNOWN
│ ↓ MyCallback.this$0
├─ com.example.MainActivity
│ Leaking: YES (MainActivity is the leak)
╰
Neste exemplo, o LeakCanary mostra que a MainActivity é retida através da cadeia: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → MainActivity novamente. A seta this$0 indica que a classe anônima MyCallback capturou uma referência externa à Activity. A solução é tornar o callback uma referência fraca ou cancelá-lo no onDestroy.
O LeakCanary também mostra o status do vazamento para cada elemento da cadeia: NO (sem vazamento — é um elemento raiz), YES (objeto deve ser destruído), UNKNOWN (não foi possível determinar o status). O status UNKNOWN não significa um problema — é um objeto intermediário que o LeakCanary não pode classificar de forma conclusiva.
A transição da versão 1.x para a 2.x foi radical: os desenvolvedores reescreveram a biblioteca do zero, substituindo o antigo analisador HAHA pelo próprio motor Shark, escrito em Kotlin. O Shark é uma ordem de grandeza mais rápido, requer menos memória para análise e determina com mais precisão as causas raiz dos vazamentos.
| Parâmetro | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| Linguagem do analisador | Java (HAHA — fork do Android SDK) | Kotlin (Shark — motor próprio) |
| Instalação | Configuração manual do AppWatcher na Application | Automática via ContentProvider |
| Velocidade | 10–30 segundos para análise de heap dump | 1–5 segundos para análise de heap dump |
| Desempenho | Ocupa 10–50 MB de RAM durante a análise | Ocupa 2–10 MB de RAM durante a análise |
A principal vantagem do Shark é que ele não carrega todo o heap dump na memória, mas percorre seu grafo de referências com alocações mínimas. Isso torna o LeakCanary 2.x adequado para uso em dispositivos com pouca RAM sem risco de OutOfMemoryError durante a análise.
A versão 2.x também introduziu a capacidade de exportar heap dumps para um arquivo para análise posterior no Android Studio Memory Profiler. Para isso, ative a configuração dumpHeapWhenLeakFound na configuração do AppWatcher.
LeakCanary detecta eficazmente várias classes de vazamentos comuns no Android. O mais frequente é o vazamento através de referências estáticas a uma Activity — desenvolvedores mantêm uma referência ao contexto da Activity em um singleton, e a Activity não pode ser coletada pelo GC após o término de seu ciclo de vida.
A segunda categoria mais comum são vazamentos através de ouvintes não registrados. Se registerListener foi chamado no onStart mas unregisterListener não foi chamado no onStop/onDestroy, o objeto ouvinte é retido pelo sistema mesmo após a atividade ser destruída. O LeakCanary mostra claramente qual ouvinte e em qual serviço do sistema permanece vivo.
// Vazamento típico: Activity capturada em um callback de singleton
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // referência forte ao callback
}
fun unregister() {
callback = null // NÃO ESQUEÇA de chamar no onDestroy!
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // lambda captura this
}
// se unregister não for chamado no onDestroy → vazamento de Activity
}
}
A terceira categoria são vazamentos através de Fragment na BackStack. Se FragmentTransaction.addToBackStack() é chamado sem remover o Fragment ao voltar, instâncias antigas do Fragment permanecem na memória. O LeakCanary ajuda a detectar esses vazamentos ocultos nos estágios iniciais do desenvolvimento.
Para cada vazamento detectado, o LeakCanary fornece uma descrição e recomendações para correção. A versão 2.14 adicionou integração com o Android Lint — a biblioteca pode criar automaticamente tarefas no rastreador de problemas quando um vazamento é detectado no CI.
Perguntas frequentes
Sim, absolutamente. O LeakCanary é adicionado via debugImplementation no build.gradle, o que o exclui automaticamente dos builds de lançamento. Se usar implementation, a biblioteca será incluída no APK de lançamento e mostrará vazamentos aos usuários finais — isso é inaceitável.
O impacto no desempenho é mínimo. O LeakCanary só é ativado após o onDestroy do componente e não interfere na renderização da UI ou no processamento de toques. O único custo é uma breve pausa forçada do GC (cerca de 100 ms) e a gravação do heap dump quando ocorre um vazamento (frações de segundo).
O LeakCanary salva automaticamente os heap dumps no formato HPROF na pasta do aplicativo. O arquivo pode ser exportado via Android Studio: Device File Explorer → data/data/com.example/files/leakcanary/. Para visualizá-lo, abra o arquivo no Memory Profiler através de Capture → Open Heap Dump.
Sim, a partir da versão 2.12 o LeakCanary é totalmente compatível com Jetpack Compose. A biblioteca rastreia contextos de Composition e objetos State, detectando automaticamente vazamentos em funções Composable. Nenhuma configuração separada é necessária — funciona imediatamente.
Falsos positivos são possíveis, mas raros. O LeakCanary usa uma chamada tripla ao GC antes de declarar um vazamento, o que elimina a maioria dos falsos positivos. Se você acredita que uma detecção é um falso positivo, crie um IgnoredReference para a classe específica na configuração.
Resumo
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.
Leia também