0Pricing
Android Academy · Aula

Vazamentos de memória e correções

Encontre e interrompa vazamentos.

Vazamentos de memória e correções é uma aula grátis de Android Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Android Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Android Academy inclui 4 aulas no total.

O que é um vazamento de memória?

Um vazamento de memória acontece quando objetos que não são mais necessários não podem ser coletados pelo coletor de lixo porque algo ainda mantém uma referência a eles. No Android, a vítima clássica é uma Activity ou um Context que permanece após a destruição da tela.

Os vazamentos aumentam sua pilha ao longo do tempo, provocam coletas de lixo mais frequentes (que pausam a interface e causam engasgos) e, por fim, fazem o aplicativo travar com um OutOfMemoryError. Nesta lição, você verá como encontrá-los e corrigi-los.

Como o GC decide o que manter

O coletor de lixo do Android mantém qualquer objeto que seja alcançável a partir de uma raiz do GC (threads ativas, campos estáticos etc.). Tudo o que não for alcançável é liberado.

Um vazamento é simplesmente um caminho de alcançabilidade indesejado: um objeto de longa duração que mantém uma referência a um objeto de curta duração. A demonstração em Kotlin puro abaixo mostra como a alcançabilidade mantém um objeto vivo.

object GlobalCache {
    val items = mutableListOf<ByteArray>()
}

fun cacheSomething() {
    // This 1MB array stays alive forever because GlobalCache
    // (a static singleton, a GC root) keeps referencing it.
    GlobalCache.items.add(ByteArray(1_000_000))
}

fun main() {
    repeat(3) { cacheSomething() }
    println("Held arrays: ${GlobalCache.items.size}") // never freed
}

O clássico: vazamento de uma atividade

O vazamento mais comum no Android ocorre quando um objeto estático ou de longa duração mantém uma referência a um contexto. Quando a atividade é destruída (por rotação ou navegação), o sistema tenta liberá-la, mas a referência estática a mantém viva para sempre.

O código abaixo causa um vazamento: um singleton armazena em cache o contexto de uma atividade.

// LEAK: static field holds an Activity Context
object Analytics {
    var context: Context? = null   // <- keeps Activity alive
    fun init(ctx: Context) { context = ctx }
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Analytics.init(this) // passing the Activity -> leak on rotation
    }
}

Correção: use o contexto da aplicação

Quando precisar armazenar um Context em algo de longa duração, armazene o contexto da aplicação, que permanece ativo durante todo o processo e pode ser mantido com segurança.

Mantenha um contexto de atividade ou de visualização somente enquanto essa tela existir. Essa regra simples evita a maioria dos vazamentos no Android.

object Analytics {
    private var appContext: Context? = null
    fun init(ctx: Context) {
        // applicationContext is process-scoped and safe to keep
        appContext = ctx.applicationContext
    }
}

// Call site is now leak-free:
Analytics.init(this) // stores applicationContext, not the Activity

Classes internas e manipuladores

Uma classe interna não estática (incluindo a maioria dos ouvintes, dos Runnables e das chamadas de retorno de Handler) mantém uma referência implícita à classe externa. Se esse trabalho continuar depois que a tela for encerrada, ele causará um vazamento da tela.

Um Handler.postDelayed atrasado é um dos responsáveis mais frequentes: a mensagem pendente mantém a atividade viva até ser executada.

// LEAK: the posted Runnable holds the Activity for 60 seconds
handler.postDelayed({ updateUi() }, 60_000)

// FIX: cancel pending work when the screen goes away
override fun onDestroy() {
    super.onDestroy()
    handler.removeCallbacksAndMessages(null)
}

Corrotinas: defina o escopo do trabalho

Uma corrotina que continua ativa depois que a tela é encerrada causa o vazamento de tudo o que captura. A solução é a concorrência estruturada: inicie o trabalho em um escopo vinculado a um ciclo de vida para que ele seja cancelado automaticamente.

Em um ViewModel, use viewModelScope; em um controlador de interface, use lifecycleScope. Quando o proprietário é destruído, o escopo cancela o trabalho e as referências são liberadas.

class FeedViewModel : ViewModel() {
    fun load() {
        // Cancelled automatically when the ViewModel is cleared
        viewModelScope.launch {
            val feed = repository.fetchFeed()
            _state.value = feed
        }
    }
}

// In Compose, collect tied to the lifecycle:
val state by viewModel.state.collectAsStateWithLifecycle()

DisposableEffect no Compose

No Compose, tudo o que você registra deve ser desregistrado quando o componente composable sair da composição. DisposableEffect fornece um bloco onDispose exatamente para isso: ouvintes, observadores, sensores e receptores de transmissão.

Esquecer a limpeza nesse ponto causa o vazamento da chamada de retorno e de tudo o que ela captura.

@Composable
fun LocationDisplay(manager: LocationManager) {
    DisposableEffect(manager) {
        val listener = LocationListener { /* update */ }
        manager.register(listener)
        onDispose { manager.unregister(listener) } // prevents the leak
    }
}

Como encontrar vazamentos com LeakCanary

LeakCanary é a ferramenta padrão para detectar vazamentos automaticamente em compilações de depuração. Adicione a dependência, e ela observará os objetos destruídos; se algum não for coletado pelo coletor de lixo, ela fará uma captura do monte de memória e mostrará a cadeia exata de referências que o mantém vivo.

Essa cadeia indica a correção: ela aponta diretamente para o campo ou ouvinte problemático.

// build.gradle.kts (app module)
dependencies {
    debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
}
// No code needed: on a debug build, navigate away from a screen and
// LeakCanary posts a notification with the leak trace, e.g.:
//   Analytics.context -> MainActivity (leaked)

Capturas do monte de memória no criador de perfis

Para vazamentos que o LeakCanary não sinaliza (crescimento lento, código nativo ou caches grandes), use o criador de perfis de memória. Capture o monte de memória, force a coleta de lixo e verifique quais classes têm uma quantidade surpreendentemente alta de instâncias.

Uma quantidade crescente de instâncias da sua atividade, fragmento ou ViewModel depois de sair da tela é uma evidência clara. O criador de perfis também mostra o caminho até as raízes da coleta de lixo, para que você possa rastrear a referência.

Bitmaps e alocações grandes

Nem todo problema de memória é um vazamento de referências. Bitmaps grandes e caches sem limite podem esgotar o monte de memória por conta própria. Uma foto em resolução máxima pode ocupar dezenas de megabytes na memória.

Permita que uma biblioteca de imagens (Coil/Glide) reduza a amostragem para o tamanho exibido e defina um limite explícito para qualquer cache que criar. O trecho mostra um cache LRU limitado por tamanho.

// Bound memory: keep at most ~1/8 of available app memory
val maxKb = (Runtime.getRuntime().maxMemory() / 1024 / 8).toInt()
val bitmapCache = object : LruCache<String, Bitmap>(maxKb) {
    override fun sizeOf(key: String, value: Bitmap) = value.byteCount / 1024
}
// An unbounded HashMap<String, Bitmap> would grow until OutOfMemoryError.

Lista de verificação para evitar vazamentos

Adote estes hábitos para que a maioria dos vazamentos nunca aconteça:

  • Armazene applicationContext em objetos de longa duração, nunca uma atividade ou visualização.
  • Execute trabalhos assíncronos em uma corrotina com escopo de ciclo de vida (viewModelScope, lifecycleScope).
  • Sempre desregistre ouvintes e receptores (onDispose, onDestroy).
  • Cancele sempre os Handlers atrasados e os temporizadores.
  • Defina limites para os caches e reduza a amostragem dos bitmaps.
  • Mantenha o LeakCanary nas compilações de depuração e aja com base nos rastreamentos.

Verificação rápida

Você precisa que um objeto singleton mantenha um Context durante todo o seu tempo de vida. Qual contexto ele deve manter para evitar o vazamento de uma tela?

Recapitulação: mantenha a memória limpa

Você aprendeu o que são vazamentos e como impedi-los:

  • Vazamentos são caminhos indesejados de alcançabilidade que mantêm objetos mortos vivos.
  • O vazamento clássico é uma referência estática ou de longa duração a uma atividade ou contexto — armazene applicationContext no lugar.
  • Ouvintes de classes internas, Handlers atrasados e corrotinas sem escopo causam o vazamento do proprietário — defina o escopo e cancele-os.
  • Use DisposableEffect no Compose para desregistrar chamadas de retorno.
  • LeakCanary e o criador de perfis de memória revelam a cadeia de referências.
  • Defina limites para os caches e reduza a amostragem dos bitmaps.

A seguir: como fazer o aplicativo iniciar rapidamente com otimização da inicialização e perfis de referência.

Perguntas Frequentes

A aula “Vazamentos de memória e correções” é grátis?

Sim — o texto completo de “Vazamentos de memória e correções” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Android Academy, atualize para CoddyKit PRO. O curso de Android Academy inclui 4 aulas no total.

O que vou aprender em “Vazamentos de memória e correções”?

Encontre e interrompa vazamentos. Você pratica Android Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Android Academy?

Nenhuma experiência prévia é necessária. Android Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Vazamentos de memória e correções”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Android Academy?

Sim. Cada aula de Android Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Medindo o desempenho
  2. Dominando a recomposição
  3. Vazamentos de memória e correções
  4. Inicialização e perfis de referência
← Voltar para Android Academy