OutOfMemoryError no desenvolvimento de aplicativos: o que é, causas e métodos de prevenção

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

OutOfMemoryError é uma exceção fatal que ocorre quando a Máquina Virtual Java (JVM) ou o Android Runtime (ART) não consegue alocar memória para um novo objeto devido à falta de espaço no Heap. De acordo com a Square Engineering, 70% dos OutOfMemoryError em aplicativos móveis são causados por vazamentos de memória, não pela ultrapassagem real do limite. Compreender as causas do OOM é a chave para a estabilidade do aplicativo.

Principais Conclusões

  • OutOfMemoryError — uma exceção quando não há Heap suficiente para criar um novo objeto
  • Heap — a área de memória onde todos os objetos Java/Kotlin vivem
  • Bitmap — o principal consumidor de Heap no Android, uma fonte típica de OOM
  • Heap Dump — um instantâneo do Heap para analisar quem ocupa quanta memória
  • Tratamento do OOM requer corrigir vazamentos e otimizar o consumo de memória

O que é OutOfMemoryError

OutOfMemoryError (OOM) é uma exceção da família VirtualMachineError em Java/Kotlin que sinaliza a incapacidade de alocar memória para um novo objeto. Diferente das exceções verificadas, OOM é um Error e não requer tratamento através de catch — embora tecnicamente possa ser capturado. Após ocorrer um OOM, o aplicativo geralmente fica em um estado instável e é recomendado encerrá-lo.

No Android, cada aplicativo tem um limite de Heap definido pelo fabricante do dispositivo. Para smartphones modernos com 6+ GB de RAM, o limite é de 256–512 MB, para dispositivos econômicos — 128–192 MB. Quando o volume total de todos os objetos vivos excede esse limite, o ART lança OutOfMemoryError.

É importante entender: OOM nem sempre significa que o dispositivo ficou sem memória física. Significa que o aplicativo esgotou seu limite de Heap definido pelo sistema. Outros aplicativos podem ter memória livre, mas seu aplicativo não pode usá-la devido ao isolamento de processos no Android.

Principais Causas do OutOfMemoryError

Cinco cenários levam regularmente ao OOM em aplicativos móveis. Cada cenário está associado a um tipo específico de dado ou operação.

Bitmap Sem Redimensionamento

Bitmap é o principal consumidor de memória em aplicativos Android. Carregar uma imagem FullHD (1920 × 1080) no tamanho original ocupa 8,3 MB no formato ARGB_8888. Se houver 50 dessas imagens em um RecyclerView, são 415 MB, excedendo o Heap de qualquer dispositivo. Carregar imagens sem inSampleSize garante OOM em dispositivos fracos.

Use Glide ou Coil para redimensionamento automático. Essas bibliotecas carregam imagens com tamanho compatível com a View, não com a resolução original. Para uso direto de BitmapFactory.Options, aplique inSampleSize: calcule-o como uma potência de dois para que o tamanho final não exceda 2048 × 2048 pixels. Adicionalmente, use RGB_565 em vez de ARGB_8888 para imagens sem transparência — isso reduz o consumo de memória pela metade.

kotlin
fun loadScaledBitmap(path: String, reqWidth: Int): Bitmap? {
    val opts = BitmapFactory.Options().apply {
        inJustDecodeBounds = true
    }
    BitmapFactory.decodeFile(path, opts)
    opts.inSampleSize = calculateSampleSize(opts.outWidth, reqWidth)
    opts.inJustDecodeBounds = false
    return BitmapFactory.decodeFile(path, opts)
}

Vazamentos de Memória (Acumulação)

Um único vazamento de alguns KB não causará OOM. Mas dezenas de vazamentos em cada tela se acumulam: cada transição de tela adiciona um vazamento, o GC não consegue liberar objetos e o Heap enche. Um padrão típico: o usuário abre e fecha a tela de perfil 20 vezes → o Heap cresce 200 MB → o aplicativo falha com OOM.

Instale o LeakCanary no projeto para detecção automática de vazamentos. Ele mostrará cada objeto vazado com um stack trace exato. Após corrigir todos os vazamentos, o consumo de Heap se torna estável: ao fechar uma tela, a memória retorna ao nível base.

Arquivos Grandes na Memória

Carregar arquivos inteiros em byte[] é um caminho direto para OOM. Um arquivo JSON de 50 MB durante a análise criará uma string do mesmo tamanho mais um modelo DOM. Arquivos de vídeo carregados na memória, buffers de áudio e grandes conjuntos de dados protobuf — todos podem exceder o limite de Heap em uma única operação.

Processe dados grandes usando fluxos: InputStream com buffer de 4–8 KB, analisador JSON em streaming (Jackson ou Gson com JsonReader), MediaCodec para vídeo. Nunca chame File.readBytes() em arquivos maiores que 10% do Heap disponível.

Criação de Muitos Objetos em um Loop

A criação intensiva de objetos em um loop sem GC intermediário pode levar ao OOM, especialmente em dispositivos com Heap pequeno. Exemplo: gerar 100.000 objetos em um for-loop que não cabem no Heap antes que o GC possa coletá-los. Isso é mais comum em jogos e editores gráficos.

Use Object Pool para objetos que são criados e destruídos em massa. Para dados numéricos, use primitivos (FloatArray em vez de List<Float>). RecyclerView com ViewHolder Pool resolve esse problema para componentes de UI.

Fragmentação do Heap

Fragmentação é um estado onde há memória livre suficiente no total, mas nenhum bloco contíguo para um novo objeto. O ART compacta o Heap durante o GC, mas nem sempre com sucesso. Arrays grandes (Bitmap, byte[]) são os mais sensíveis à fragmentação.

O ART no Android 8+ usa GC Geracional, que reduz a fragmentação separando objetos jovens e velhos. No entanto, evite alocar fragmentos de diferentes tamanhos no mesmo pool — tente usar buffers pré-alocados de tamanho fixo.

Limites de Heap no Android

O limite de Heap no Android não é uma constante — depende do fabricante, modelo do dispositivo e versão do SO. O Google define requisitos mínimos através do Documento de Definição de Compatibilidade (CDD), mas os fabricantes definem os valores reais.

Categoria do DispositivoHeap TípicolargeHeap
Econômico (1–2 GB RAM)128–192 MB256–384 MB
Intermediário (3–4 GB RAM)256–384 MB512 MB
Flagship (6+ GB RAM)384–512 MB768 MB–1 GB
Tablets (4+ GB RAM)256–512 MB768 MB
Wear OS32–64 MBN/A

Você pode solicitar um limite aumentado através de android:largeHeap="true" no manifesto. Use com cuidado: aumentar o Heap não resolve o problema de vazamentos e pode piorar a experiência do usuário se o sistema for forçado a matar outros aplicativos para liberar memória para o seu. Para Wear OS, o limite de Heap é mínimo — apenas 32–64 MB, largeHeap não está disponível aqui, e a economia de memória é duplamente crítica.

Diagnosticando OutOfMemoryError

Diagnosticar OOM requer analisar um Heap Dump e entender quais objetos consomem memória. O Android Studio fornece todas as ferramentas necessárias.

Passo 1: Capture o momento do OOM. No Android Memory Profiler, clique em Record memory allocations e execute o cenário que causa a falha. O Profiler mostrará um pico nas alocações antes do OOM. Se o OOM não for reproduzível, reduza o Heap via android:smallHeap na compilação de depuração ou use DDMS com invocação manual de GC.

Passo 2: Tire um Heap Dump no pico de carga (antes do OOM). Abra o Dump no Android Studio: a aba Classes está ordenada por Retained Size. Os maiores objetos são Bitmap, byte[], String. Para cada Bitmap, verifique o tamanho (largura × altura × 4 bytes) e o caminho de carga via Stack Trace.

Passo 3: Analise a quantidade de objetos duplicados. Se você vir 200 Fragment ou Activity idênticos — isso é um vazamento. Se 500 Bitmaps com o mesmo tamanho — é um problema de cache de imagens. O MAT (Memory Analyzer Tool) fornece uma análise mais profunda com uma Dominator Tree mostrando quais objetos retêm 80% do Heap.

text
// Comando de Heap Dump via adb
adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof.

Estratégias de Prevenção de OOM

Uma estratégia abrangente de prevenção de OOM inclui cinco níveis de proteção: desde decisões arquitetônicas até monitoramento em produção.

Decisões Arquitetônicas

ViewModel + Repository separa os dados da UI e impede a retenção da View ao girar a tela. A ViewModel sobrevive à Activity, seus dados não são perdidos e a View pode ser recriada sem duplicar dados na memória. Use StateFlow em vez de LiveData para gerenciamento explícito de estados.

Gerenciamento de Bitmap e Imagens

Glide é uma biblioteca obrigatória para trabalhar com imagens. Ela automaticamente redimensiona, armazena em cache (disco + memória) e recicla Bitmap. Configure diskCacheStrategy e skipMemoryCache para listas grandes. Para imagens animadas, use Glide com GIF/WebP — eles ocupam menos memória que uma sequência de Bitmaps.

Monitoramento em Produção

Firebase Performance Monitoring rastreia o consumo de memória em tempo real. Defina um alerta quando o uso do Heap exceder 80% do limite — é um sinal para investigar. O Crashlytics coleta OOM como uma exceção e mostra o último estado conhecido do Heap antes da falha. Para Android 11+, use ApplicationExitInfo para detectar terminações por OOM.

Testes em Dispositivos Fracos

Certifique-se de testar o aplicativo em dispositivos com Heap mínimo (128–192 MB). Um emulador com tela pequena e Heap pequeno emula um dispositivo econômico. Se o aplicativo funcionar nesse dispositivo, não haverá problemas de OOM em flagships. Use o Firebase Test Lab com dispositivos reais de diferentes categorias de preço.

kotlin
// Verificação do Heap disponível antes de operação pesada
fun canAllocate(requiredBytes: Long): Boolean {
    val runtime = Runtime.getRuntime()
    val free = runtime.freeMemory()
    return free > requiredBytes * 2 // margem de 50%
}

Perguntas Frequentes

Pode-se capturar OutOfMemoryError com try-catch?

Tecnicamente sim, mas não é recomendado. Após o OOM, o aplicativo está em um estado instável: novas alocações podem falhar e alguns objetos podem estar parcialmente criados. A única ação razoável no catch é registrar e reiniciar a Activity.

Por que o OOM não ocorre em todos os dispositivos?

O limite de Heap varia entre dispositivos. Uma operação que requer 300 MB falhará em um dispositivo com limite de 192 MB, mas terá sucesso em um flagship com 512 MB. Teste em dispositivos com especificações mínimas para detectar cenários de OOM.

Como o largeHeap afeta o desempenho?

largeHeap aumenta o limite, mas não acelera o aplicativo. As pausas do GC se tornam mais longas, pois coletar um Heap grande leva mais tempo. O sistema pode matar aplicativos em segundo plano para fornecer memória. Use largeHeap apenas para aplicativos que objetivamente precisam de muita memória (câmeras, editores).

Como o OOM difere de uma morte do sistema?

OOM é uma exceção dentro de um aplicativo quando o Heap é insuficiente. A morte do sistema (Low Memory Killer) é uma decisão do kernel Linux de matar um processo para liberar memória para outros aplicativos. Em uma morte do sistema, o aplicativo não recebe uma exceção — o processo simplesmente termina.

Quanta memória um Bitmap realmente consome?

A fórmula: largura × altura × bytesPerPixel. ARGB_8888 = 4 B/pixel, RGB_565 = 2 B/pixel. Um Bitmap FullHD (1920 × 1080) em ARGB_8888 = 8,3 MB. Um Bitmap 4K (3840 × 2160) = 33 MB. Sempre redimensione as imagens para o tamanho necessário para exibição na tela.

Resumo

  • OutOfMemoryError — uma exceção fatal quando o limite de Heap do aplicativo é esgotado
  • Bitmap sem redimensionamento — o principal culpado de OOM em aplicativos móveis
  • Vazamentos de memória causam 70% dos OOM através da acumulação de objetos em cada transição
  • Limite de Heap varia de 128 MB em dispositivos econômicos a 512 MB em flagships
  • Heap Dump com análise de Retained Size — a principal ferramenta para diagnosticar OOM
  • Glide ou Coil são obrigatórios para trabalhar com imagens de qualquer tamanho
  • Testes em dispositivos com Heap mínimo são indispensáveis para todos os projetos

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