Vazamento de memória — um dos problemas mais insidiosos no desenvolvimento móvel. O uso de memória do aplicativo cresce continuamente até atingir o limite definido pelo SO, seguido por um OutOfMemoryError ou encerramento forçado. De acordo com a Square Engineering, cerca de 40% dos aplicativos Android têm pelo menos um vazamento de memória que só pode ser detectado por meio de perfilamento. Vamos examinar as causas e os métodos para prevenir o crescimento da memória.
Pontos Principais
Vazamento de memória — uma situação em que um objeto que não é mais necessário ao aplicativo continua sendo mantido no heap porque uma referência ativa do conjunto raiz (GC Root) ainda aponta para ele. O coletor de lixo considera esse objeto vivo e não o remove.
Inchaço de memória — um problema mais amplo em que o aplicativo consome mais memória do que o necessário para realizar suas tarefas atuais. Causas: cache excessivo, duplicação de objetos, estruturas de dados subótimas e fragmentação do heap.
No Android, cada aplicativo recebe um heap limitado (geralmente 64–512 MB, dependendo do dispositivo e da versão do SO). No iOS, o limite é menos rigoroso, mas o sistema envia um aviso de memória ao se aproximar do limite.
| Característica | Android | iOS |
|---|---|---|
| Limite do heap | 64–512 MB (depende do dispositivo) | Implícito (sistema) |
| Coleta de lixo | ART (Concorrente, Compacto) | ARC (Contagem Automática de Referências) |
| Mecanismo de vazamento | Referências GC Root | Retain cycles (ciclos de referência forte) |
| Resultado | OutOfMemoryError | Aviso de memória → encerramento |
De acordo com o Facebook Engineering Blog, vazamentos de memória causam ~15% dos relatos de crash em aplicativos móveis. No Android, isso é agravado por ANRs devido a pausas frequentes do GC quando a memória está baixa.
Referência estática a Activity — um vazamento clássico do Android. Se um campo estático ou singleton mantém uma referência a uma Activity, ela não será coletada pelo GC mesmo após finish() enquanto o singleton estiver vivo. Uma Activity é um objeto pesado que contém uma hierarquia de views, recursos e Context.
object LeakHolder {
var activityRef: Activity ?= null // leak: static reference to Activity
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle ?= null) {
super.onCreate(savedInstanceState)
LeakHolder.activityRef = this // ❌ MainActivity will never be GC'd
}
}
Classes anônimas e lambdas — retêm implicitamente uma referência à classe externa. Se um Runnable ou Callback é passado para um serviço externo e a Activity é destruída, o objeto da classe anônima ainda está na fila e impede que a Activity seja coletada como lixo.
No iOS, o principal problema são os retain cycles: dois objetos mantêm referências fortes entre si e o ARC não pode zerar a contagem de referências de nenhum deles. Um caso típico: um closure que captura self fortemente e self que mantém uma referência ao closure.
LeakCanary — uma biblioteca da Square para detecção automática de vazamentos no Android. Após a destruição de uma Activity ou Fragment, ela verifica se o objeto foi coletado pelo GC. Se não, faz um heap dump e mostra o trace do vazamento.
// LeakCanary 2.x — auto-integration via Application
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// LeakCanary auto-installs in debug build
// via ContentProvider — zero code setup
}
}
// Force check invocation
AppWatcher.objectWatcher.watch(watchedObject, "leak description")
Android Studio Profiler — uma ferramenta integrada para monitoramento de memória em tempo real. Permite gravar um heap dump, encontrar objetos suspeitos (Retained Size > 1 MB) e rastrear o caminho GC root até cada objeto.
Para iOS, use o Xcode Memory Graph Debugger. Ele visualiza o grafo de objetos na memória, mostra retain cycles e permite detectar instantaneamente referências circulares. Instruments > Allocations também está disponível para monitoramento de longo prazo.
WeakReference — um mecanismo básico para referências que não devem interferir na coleta de lixo. Se o GC decidir coletar um objeto, WeakReference retorna null. É usado para callbacks, listeners e referências a componentes de UI de threads em segundo plano.
Componentes Lifecycle-aware — uma abordagem arquitetural implementada no Android Jetpack (Lifecycle, LiveData, Flow, coroutines). As assinaturas são canceladas automaticamente no onDestroy, eliminando a principal classe de vazamentos.
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<List<User>>()
val data: LiveData<List<User>> get() = _data
fun loadData() {
viewModelScope.launch {
val result = repository.fetchData()
_data.postValue(result)
// coroutine auto-cancels on onCleared()
}
}
}
viewModelScope e lifecycleScope — CoroutineScope integrados no Android que são cancelados no evento de ciclo de vida correspondente. Isso elimina vazamentos por meio de corrotinas — o cenário mais comum no desenvolvimento Android moderno.
Memory Profiler no Android Studio — a ferramenta principal para monitoramento do heap. Mostra alocações ao vivo, instantâneos do heap e contagens de objetos por tipo. Permite gravar um dump e analisá-lo no MAT (Memory Analyzer Tool) para encontrar objetos suspeitos.
Eclipse MAT — um analisador de heap dump de desktop. Após carregar um arquivo HPROF do Android Studio, o MAT constrói uma árvore dominadora, mostra o retain size de cada objeto e oferece análise automática de vazamentos suspeitos através do Leak Suspects Report.
Xcode Memory Graph — um depurador visual de retain cycles. Ao clicar no botão Memory Graph Debugger, o Xcode para o aplicativo, constrói um grafo completo de objetos na memória e destaca retain cycles em vermelho.
| Ferramenta | Plataforma | Característica |
|---|---|---|
| LeakCanary | Android | Detecção automática de vazamentos após destroy |
| Memory Profiler | Android Studio | Heap dump + alocações ao vivo |
| Eclipse MAT | Android | Árvore dominadora, Leak Suspects Report |
| Memory Graph | iOS (Xcode) | Visualizador de retain cycles |
De acordo com o Google I/O 2023, aplicativos que usam LeakCanary em builds de depuração reduzem crashes relacionados à memória em 30–50% nos primeiros 2 meses após a adoção. Recomenda-se adicionar o LeakCanary durante a fase de integração do projeto.
Perguntas Frequentes
Vazamento — objetos inacessíveis pelo código mas não coletados pelo GC devido a referências ativas. Inchaço — o aplicativo mantém objetos logicamente necessários mas em quantidade excessiva (por exemplo, um cache de 50 MB em um aplicativo em execução de 80 MB). O inchaço é resolvido arquiteturalmente; o vazamento, através do gerenciamento correto de referências.
LeakCanary usa ObjectWatcher — após onDestroy() de uma Activity, ele cria uma WeakReference para a Activity e executa o GC. Se a WeakReference não for limpa após 5 segundos, o LeakCanary faz um heap dump, analisa a cadeia de referência mais curta do GC Root ao objeto e mostra o stack exato do vazamento com o arquivo e linha de código.
Bitmap ocupa memória fora do heap Java na memória nativa (native heap). O tamanho de um Bitmap = largura × altura × 4 bytes (ARGB_8888). Uma foto de 12 MP (4000×3000) ocupa 48 MB. O Android nem sempre consegue liberar a memória nativa a tempo, então o acúmulo de vários Bitmaps leva a OOM mesmo com heap Java suficiente.
Retain cycle — uma situação no ARC onde dois objetos mantêm referências fortes entre si e a contagem de referências nunca chega a zero. Um exemplo típico: um ViewController com uma referência forte a um closure, e o closure captura self fortemente. Solução: use [weak self] ou [unowned self] nos closures.
O tamanho do heap depende do dispositivo e da versão do Android. Para dispositivos antigos (API 15–24) — 64–128 MB. Para os modernos (API 25+) — 256–512 MB. O valor exato pode ser obtido através de ActivityManager.getMemoryClass(). Para aplicativos grandes (jogos, editores), largeHeap=true no manifesto fornece até 1 GB.
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