O WebView executa código nativo em vários processos para renderizar conteúdo da Web
no seu app Android. Deixar instâncias WebView sem gerenciamento pode levar a vazamentos de memória,
falhas por falta de memória (OOM) e desempenho degradado do app.
Este documento explica o modelo de memória multiprocesso do WebView, descreve como gerenciar corretamente o ciclo de vida dele para evitar vazamentos e fornece fluxos de trabalho práticos para diagnosticar problemas de memória.
Entender a arquitetura de memória do WebView
Para gerenciar a memória WebView de maneira eficaz, entenda como o Android aloca
recursos para conteúdo da Web:
Execução multiprocesso:no Android 8.0 (nível 26 da API) e versões mais recentes, o
WebViewsepara o conteúdo da Web das funções principais do app em vários processos. Em dispositivos com pouca RAM, ele pode voltar a um único processo:- Processo do host (navegador):é o processo principal do app em que seu
Activitye o código Java ou Kotlin são executados. - Processo de renderização isolado:um processo separado no modo sandbox
(
SandboxedProcessService) que analisa HTML e CSS, executa JavaScript e renderiza páginas da Web.
- Processo do host (navegador):é o processo principal do app em que seu
Consumo de memória nativa:a maior parte da memória
WebView, incluindo gráficos renderizados, a árvore do DOM e a memória de tempo de execução do JavaScript, é alocada na memória nativa, não no heap Java. Um heap dump do Java (.hprof) mostra apenas um objeto wrapper Java leve e não captura a memória real usada pelo conteúdo da Web.Impacto no sistema da memória nativa:ao contrário das alocações de heap Java, que são limitadas pelo limite
maxHeapdo app e falham rapidamente com umOutOfMemoryError, a memória nativa pode crescer silenciosamente até gigabytes. À medida que a memória nativa não liberada preenche a RAM física e o espaço de troca (zRAM), o Low Memory Killer (LMK) do Android começa a encerrar processos em segundo plano para recuperar memória. Isso degrada a multitarefa geral do dispositivo antes de encerrar o app em primeiro plano.
Gerenciar o ciclo de vida do WebView
O gerenciamento adequado do ciclo de vida é essencial para evitar vazamentos de memória. Um erro comum é presumir que remover um WebView do layout ou deixar um Activity terminar automaticamente libera a memória dele.
Para garantir a limpeza completa das referências de contexto Java e dos recursos de renderização nativa, coordene explicitamente uma sequência de desmontagem no ciclo de vida do componente host (como onDestroy()), interrompendo a execução da página ativa, separando a visualização do contêiner e liberando vinculações nativas.
Limpar instâncias do WebView
Para garantir um desligamento limpo e liberar recursos quando o Activity ou
Fragment for destruído, faça o seguinte:
- Remova o
WebViewdo contêiner principal (ViewGroup). - Parar o carregamento ativo e limpar o histórico de navegação.
- Chame o método
destroy(). - Limpe a referência a
null.
O exemplo a seguir demonstra como limpar corretamente um WebView:
Kotlin
override fun onDestroy() { myWebView?.let { // Remove the WebView from its parent ViewGroup. (it.parent as? ViewGroup)?.removeView(it) // Stop active loading and clear history. it.stopLoading() it.clearHistory() // Destroy the instance. it.destroy() } myWebView = null super.onDestroy() }
Java
@Override
protected void onDestroy() {
if (myWebView != null) {
// Remove the WebView from its parent ViewGroup.
if (myWebView.getParent() instanceof ViewGroup) {
((ViewGroup) myWebView.getParent()).removeView(myWebView);
}
// Stop active loading and clear history.
myWebView.stopLoading();
myWebView.clearHistory();
// Destroy the instance.
myWebView.destroy();
}
myWebView = null;
super.onDestroy();
}
Entender a memória pós-destruição
Quando você chama destroy(), o sistema libera o contexto Activity, limpa
hierarquias de visualização e interrompe o trabalho em segundo plano da Web. No entanto, talvez você observe que a memória física do processo (tamanho do conjunto residente) não cai imediatamente para o valor de referência pré-WebView.
Isso é normal. Os caches de tempo de execução nativo, as bibliotecas compartilhadas e as páginas de memória alocadas permanecem residentes no processo até que o sistema operacional as recupere ou o processo seja encerrado. O objetivo principal do destroy() é evitar
vazamentos de memória Activity cumulativos quando os usuários navegam dentro e fora de telas
com tecnologia da Web.
Principais métricas de depuração
Ao analisar o consumo de memória do WebView, concentre-se nas seguintes métricas:
Resident Set Size (RSS): o total de RAM física mapeada no processo, incluindo código e bibliotecas compartilhados (rotulados como Total no Android Studio Profiler).
RSS anônimo (RssAnon): memória alocada diretamente pelo processo que não é protegida por um arquivo no disco (como heap nativo e alocações de tempo de execução do JavaScript). Isso representa o custo principal de memória do seu conteúdo da Web (marcado como Alocado no Android Studio Profiler).
Consumo de memória privada (PMF): a soma do RSS anônimo e do espaço de troca (zRAM). A PMF reflete a carga real de memória não removível que seu app impõe ao sistema.
PMF do navegador x PMF do renderizador:memória usada pelo processo principal do app x memória usada pelo processo de renderização isolado. Conteúdo da Web pesado causa picos principalmente no processo do renderizador.
Contagens de objetos ativos (
WebViews,Activities,Views): o número de instâncias ativas de interface, contexto eWebViewmantidas na memória. O rastreamento identifica se o crescimento da memória é causado por referências Java retidas ou alocações somente nativas.Heap nativo e outro particular:em
dumpsys meminfo, as alocações nativas em C/C++ e os mapeamentos de memória personalizados (comoPartitionAllocdo Chromium ou heaps de tempo de execução JavaScript incorporados) aparecem em "Heap nativo" e "Outro particular" em vez de "Heap Java".
Para mais informações sobre contadores de memória de processo e categorias, consulte o Glossário de memória de processo.
Fluxos de trabalho de diagnóstico práticos
Como o WebView opera em vários processos e aloca memória nativa, use as seguintes ferramentas e técnicas para inspecionar a pegada dele:
Ferramentas de criação de perfil e diagnóstico
Para inspecionar alocações de memória e diagnosticar vazamentos, use as seguintes ferramentas:
Memory Profiler do Android Studio:use o Memory Profiler para visualizar alocações nativas, rastrear categorias de memória ao longo do tempo e detectar vazamentos de
Activityem transições de tela.Rastreamento de memória com o Perfetto:use o Perfetto para gravar contadores de memória no nível do sistema (como RSS e RSS anônimo) e observar o crescimento geral da memória. Observação: as alocações do mecanismo nativo
WebViewnão produzem callstacks na ferramenta de criação de perfil de heap do Perfetto. Use o Chrome DevTools para inspecionar snapshots de heap do JavaScript e alocações do DOM no conteúdo da Web.
Inspecionar contagens de objetos ativos
Para determinar se o crescimento da memória é causado por objetos retidos do framework Java (como componentes de UI) ou alocações nativas, inspecione a seção Objects
de dumpsys meminfo:
adb shell dumpsys meminfo -a <var>PACKAGE_NAME</var> | grep -A 10 "Objects"A saída mostra as contagens de objetos ativos:
Objects
Views: 142 ViewRootImpl: 1
AppContexts: 3 Activities: 1
Assets: 12 AssetManagers: 0
Local Binders: 32 Proxy Binders: 45
Parcel memory: 15 Parcel count: 30
Death Recipients: 2 WebViews: 1
Esta seção mostra as contagens de objetos de framework ativos, manipuladores de IPC e alocações de parcelas. Para diagnósticos de WebView, concentre-se principalmente em Activities
e WebViews.
Execute a interação do usuário desejada (como abrir e fechar uma tela da Web) repetidamente e compare as contagens:
Vazamento de instância:se
WebViewsouActivitiesaumentar a cada navegação e não retornar ao valor de referência, o app está vazando a instânciaWebViewdo Java ou o hostActivity(por exemplo, devido a umViewGroup.removeView()ausente ou referências de listener retidas). Como umActivityvazado fixa toda a árvore de visualização e os recursos de imagem decodificados na memória, visitas repetidas vão esgotar rapidamente o heap Java e causar falhas deOutOfMemoryError.Vazamento nativo ou de DOM:se
WebViewseActivitiespermanecerem constantes enquanto o RSS total do processo e Private Other continuarem aumentando, o vazamento vai se originar em recursos nativos não lançados, elementos DOM ou vinculações do mecanismo JavaScript. Como essas alocações residem na memória nativa e ignoram o coletor de lixo do ART, elas permanecem invisíveis para as ferramentas padrão de detecção de vazamento do Java e continuam se acumulando até que o sistema operacional encerre o app.
Criar perfil do processo de renderização isolado usando a CLI
Executar dumpsys meminfo com o nome do pacote do app só gera memória para o processo principal do host. Para inspecionar o processo de renderização isolado em que as páginas da Web
são renderizadas:
Encontre o ID do processo (PID) do serviço de renderização isolado:
adb shell dumpsys activity processes <var>PACKAGE_NAME</var> | grep "Isolated.*SandboxedProcessService"A saída mostra o registro do processo isolado e o PID RENDERER_PID (por exemplo,
22155):Isolated #5: ProcessRecord{... 22155:com.google.android.webview.debug:sandboxed_process0:...}Inspecione o detalhamento da memória do processo de renderização usando o PID dele:
adb shell dumpsys meminfo <var>RENDERER_PID</var>Inspecione o processo do app host para avaliar a pegada do lado do navegador:
adb shell dumpsys meminfo <var>PACKAGE_NAME</var>
Inspecionar mapas e alocações de memória
Para saber quais subsistemas ou alocadores nativos ocupam a memória anônima, inspecione os mapas de memória do processo:
adb shell "cat /proc/$(pidof <var>PACKAGE_NAME</var>)/maps" | grep "anon:"A tabela a seguir lista tags de memória anônimas comuns e a relevância delas para o aumento do uso de memória:
| Tag de memória | Subsistema | Relevância para o conteúdo do app e da Web | Causa comum do aumento de memória? |
|---|---|---|---|
[anon:partition_alloc] |
PartitionAlloc do Chromium | Alocações para árvores DOM, buffers de renderização, heap JavaScript V8 e execução do WebAssembly em WebView. |
Sim (alto): o carregamento de páginas da Web pesadas, DOMs ricos em mídia ou a falha ao chamar destroy() em instâncias WebView descartadas aumenta diretamente essa tag. |
[anon:scudo...] ou [anon:libc_malloc] |
Alocadores de heap nativos do Android (Scudo / jemalloc) | Alocações nativas gerais de C/C++ usadas por bibliotecas do NDK, pontes JNI e pipelines gráficos nativos. | Sim (moderado a alto): o crescimento ocorre quando wrappers JNI nativos ou dependências C++ de terceiros retêm alocações não lançadas em navegações. |
[anon:...] (por exemplo, [anon:quickjs_heap...]) |
Scripts personalizados ou ambientes de execução nativos | Mecanismos JavaScript incorporados, ambientes de execução WebAssembly personalizados ou pools de buffers nativos personalizados. | Sim (dependente do contexto): comum em apps híbridos que executam mecanismos de script com visualizações nativas e não limpam as vinculações de tempo de execução. |
Limitações das APIs de memória no app
As APIs de memória no app (como Debug.getMemoryInfo ou
ActivityManager.getProcessMemoryInfo) medem apenas o processo de chamada.
No modo multiprocesso, essas APIs não podem capturar a memória consumida pelo
processo de renderização isolado. Para uma avaliação precisa da memória total, use ferramentas do sistema como dumpsys meminfo, Perfetto ou Android Studio Profiler.
Triagem de uso alto de memória em um app híbrido
Ao diagnosticar um crescimento inexplicável da memória durante interações recorrentes do WebView (como abrir links da Web ou navegar por feeds com tecnologia da Web), use
o fluxo de trabalho de triagem a seguir para isolar se o vazamento se origina na
camada Java ou no mecanismo nativo:
Isolar o tipo de vazamento (Java x nativo): execute
dumpsys meminfo -a <var>PACKAGE_NAME</var> | grep -A 10 "Objects"antes e depois de transições repetidas do usuário (como abrir e fechar artigos da Web ou deslizar pelos feeds).- Observação:se as contagens de
ActivitieseWebViewspermanecerem estáveis (por exemplo, de uma a duas instâncias ativas), o app não estará vazando contextos deActivitynem instâncias deWebViewdo Java.
- Observação:se as contagens de
Medir o delta de memória em várias interações (rastreamento de série temporal): capture snapshots de
dumpsys meminfoem várias interações do usuário para calcular a taxa de alocação por transição:- Observação:o heap do Java permanece limitado e íntegro (aumentando durante o uso e diminuindo após a coleta de lixo), mas Outro particular e Heap nativo aumentam constantemente em vários megabytes por transição. Isso
prova que o vazamento está totalmente na memória nativa fora do tempo de execução do ART.
Os despejos de heap Java padrão (
.hprof) não mostram problemas.
- Observação:o heap do Java permanece limitado e íntegro (aumentando durante o uso e diminuindo após a coleta de lixo), mas Outro particular e Heap nativo aumentam constantemente em vários megabytes por transição. Isso
prova que o vazamento está totalmente na memória nativa fora do tempo de execução do ART.
Os despejos de heap Java padrão (
Inspecionar mapas de memória anônimos:examine os mapas de memória do processo usando o ADB (consulte Inspecionar mapas de memória e alocações):
adb shell "cat /proc/$(pidof <var>PACKAGE_NAME</var>)/maps" | grep "anon:"- Observação:o crescimento da memória se concentra em
[anon:partition_alloc]ou em heaps do mecanismo de script incorporado, acompanhado de um aumento lento nas referências globais de JNI. Isso indica que, embora as visualizações do Java tenham sido substituídas, os objetos de página nativos ou as vinculações do JavaScript subjacentes não foram liberados.
- Observação:o crescimento da memória se concentra em
Remediação:
- Verifique se cada
WebViewreciclado ou descartado interrompe explicitamente scripts ativos (stopLoading()), limpa o histórico e chamadestroy(). - Desfaça callbacks de ponte JavaScript personalizados ou referências globais de JNI associadas a visualizações dispensadas.
- Confirme se
Private Othere o RSS do processo se estabilizam após as transições de navegação.
- Verifique se cada
Outros recursos
Para saber mais sobre depuração e criação de perfil de memória e desempenho de WebView,
consulte os seguintes recursos: