1

Como contornei estouro de memória (OOM) decifrando mídias pesadas 100% em RAM no mobile (+ a saga dos 14 dias do Google)

Fala, pessoal do TabNews!

Estou desenvolvendo o CriptoVéu, uma aplicação open-source focada em operações criptográficas e esteganografia estritamente client-side (Zero-Knowledge). A premissa central de segurança do projeto é não enviar dados para servidores e não gravar arquivos descriptografados em disco temporário.

Tudo precisa ser decifrado, manipulado e exibido diretamente na memória volátil (RAM).

No navegador desktop, alocar centenas de megabytes em memória para decifrar e renderizar um vídeo ou áudio é relativamente tolerável. No entanto, ao empacotar a aplicação para Android via WebViews/Capacitor, esbarrei em gargalos severos de Out Of Memory (OOM) ao tentar pré-visualizar arquivos de 200 MB a 400 MB.

Quero compartilhar as abordagens práticas que utilizei para solucionar esse problema de consumo de memória, a arquitetura de monetização híbrida e um convite para quem puder me dar uma força na fase de testes fechados.


  1. O problema: WebViews mobile e cópias redundantes de memória

No fluxo inicial, a descriptografia ocorria dentro de um Web Worker para não congelar a thread principal da interface. O fluxo clássico era:

  1. A thread principal lê o arquivo criptografado via FileReader / ArrayBuffer.
  2. O dado é enviado para o Worker via worker.postMessage().
  3. O Worker processa o AES-256-GCM e envia o buffer decifrado de volta via postMessage().
  4. A thread principal cria um Blob e gera um URL.createObjectURL() para o elemento <video> ou áudio.

O gargalo: O postMessage padrão utiliza o algoritmo de clonagem estruturada (Structured Clone Algorithm). Isso significa que, para um arquivo de 300 MB, a memória instantânea atingia:

  • 300 MB (buffer original) + 300 MB (cópia no Worker) + 300 MB (buffer decifrado no Worker) + 300 MB (cópia na UI) + memória do Blob.

Em aparelhos intermediários com limites de alocação de heap reduzidos no WebView, o aplicativo sofria crash imediato por OOM.


2. A solução técnica

A. Transferable Objects (Zero-Copy)

Em vez de clonar o buffer entre threads, passei a transferir a propriedade da memória:

// Transferindo da UI para o Worker sem duplicar o buffer:
worker.postMessage({ type: 'DECRYPT', buffer: payloadBuffer }, [payloadBuffer]);

// E no retorno do Worker para a UI:
self.postMessage({ type: 'SUCCESS', decryptedBuffer: resultBuffer }, [resultBuffer]);

Ao passar o ArrayBuffer no segundo argumento do postMessage, a thread de origem perde o acesso ao dado e a referência é transferida diretamente em tempo de execução, reduzindo a alocação de memória para praticamente o tamanho estrito do arquivo.

B. Processamento e Streaming em Chunks de 2 MB

Em vez de tentar processar um arquivo monolítico de uma só vez, a cifra foi adaptada para trabalhar em blocos de 2 MB com cálculo incremental de integridade. Isso permitiu que o consumo de pico da CPU e da RAM se mantivesse plano ao longo do processamento.

C. Gerenciamento manual do ciclo de vida dos Blobs

O Garbage Collector do JavaScript frequentemente atrasa a coleta de referências criadas com URL.createObjectURL(). Implementei a destruição explícita com URL.revokeObjectURL() assim que o elemento de mídia dispara o evento loadeddata ou quando o usuário fecha o modal de pré-visualização, liberando a memória do sistema operacional imediatamente.


3. Apoio voluntário e conformidade com lojas

Para manter o projeto livre de anúncios e rastreadores, estruturei um fluxo de apoio voluntário agnóstico à plataforma:

  • Na versão Web (PWA): Pagamentos espontâneos desacoplados via Stripe.
  • No aplicativo Android: Para não violar as diretrizes da Google Play (que barram meios de pagamento externos para compras dentro do app), integrei o Google Play Billing utilizando o SDK do RevenueCat, tratando gorjetas como produtos consumíveis de suporte.

4. Preciso de uma força: A barreira dos 14 dias do Google Play

Assim como outros devs aqui que publicam em contas recentes de pessoa física, estou enfrentando o requisito do Google de manter no mínimo 12 testadores com o aplicativo instalado por 14 dias contínuos para poder liberar para produção.

Se você gosta de ferramentas de privacidade/segurança, quer testar a estabilidade do app no seu aparelho ou simplesmente puder dar uma força para ajudar a bater essa meta:

  1. Entre no grupo de testadores: https://groups.google.com/g/criptoveu-testers
    (Basta entrar com a mesma conta Google que você usa na Play Store)
  2. Confirme o opt-in na Web: https://play.google.com/apps/testing/com.criptoveu.app
  3. Baixe o app na Play Store: https://play.google.com/store/apps/details?id=com.criptoveu.app

O código-fonte completo está disponível para quem quiser auditar ou contribuir:

Feedbacks sobre a arquitetura de criptografia, manipulação de streams ou problemas de performance no Android são muito bem-vindos!

Carregando publicação patrocinada...