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.
- 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:
- A thread principal lê o arquivo criptografado via
FileReader/ArrayBuffer. - O dado é enviado para o Worker via
worker.postMessage(). - O Worker processa o
AES-256-GCMe envia o buffer decifrado de volta viapostMessage(). - A thread principal cria um
Blobe gera umURL.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:
- 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) - Confirme o opt-in na Web: https://play.google.com/apps/testing/com.criptoveu.app
- 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:
- Repositório no GitHub: https://github.com/Alexh0102/Projeto_Criptoveu
- Versão Web: https://www.criptoveu.com/
Feedbacks sobre a arquitetura de criptografia, manipulação de streams ou problemas de performance no Android são muito bem-vindos!
Fonte: https://www.criptoveu.com/