Saiba como usar o Chrome e o DevTools para encontrar problemas de memória que afetam o desempenho da página, incluindo vazamentos de memória, ocupação excessiva de memória e coletas de lixo frequentes.
Resumo
- Saiba quanta memória sua página está usando com o Gerenciador de tarefas do Chrome.
- Confira o uso de memória ao longo do tempo com as gravações da linha do tempo.
- Identifique árvores do DOM desconectadas (uma causa comum de vazamentos de memória) com os resumos de pilha.
- Descubra quando nova memória é alocada à pilha do JS com as gravações da Allocation Timeline.
- Identifique elementos separados retidos pela referência do JavaScript.
Visão geral
De acordo com o modelo de desempenho RAIL, os esforços de desempenho devem se concentrar nos usuários.
Os problemas de memória são importantes porque são frequentemente percebidos pelos usuários. Os usuários podem perceber problemas de memória das seguintes maneiras:
- O desempenho de uma página piora progressivamente ao longo do tempo. Isso é possivelmente um sintoma de vazamento de memória. Um vazamento de memória ocorre quando um bug na página faz com que ela use progressivamente mais e mais memória com o tempo.
- A performance de uma página é consistentemente ruim. Isso é possivelmente um sintoma de ocupação excessiva da memória. A ocupação excessiva da memória ocorre quando uma página usa mais memória do que o necessário para obter a maior velocidade.
- O desempenho de uma página atrasa ou parece pausar com frequência. Isso é possivelmente um sintoma de coleta de lixo frequente. A coleta de lixo ocorre quando o navegador recupera memória. O navegador decide quando isso acontece. Durante as coletas, toda execução de script é pausada. Portanto, se o navegador estiver coletando muito lixo, a execução de scripts será pausada muitas vezes.
Ocupação excessiva de memória: quanto é "demais"?
É fácil definir um vazamento de memória. Se um site estiver usando progressivamente mais e mais memória, há um vazamento. Mas a ocupação excessiva da memória é um pouco mais difícil de detectar. O que é considerado "uso excessivo de memória"?
Não existem números padrão porque dispositivos e navegadores diferentes têm recursos distintos. A mesma página que funciona bem em um smartphone de ponta pode falhar em um smartphone mais simples.
A saída é usar o modelo RAIL e manter o foco nos usuários. Descubra quais dispositivos são populares com seus usuários e teste sua página neles. Se a experiência for consistentemente ruim, a página poderá estar excedendo os recursos de memória desses dispositivos.
Monitorar o uso de memória em tempo real com o gerenciador de tarefas do Chrome
Use o gerenciador de tarefas do Chrome como ponto de partida para a investigação do problema de memória. O gerenciador de tarefas é um monitor em tempo real que informa quanta memória uma página está usando.
Pressione Shift + Esc ou acesse o menu principal do Chrome e selecione Mais ferramentas > Gerenciador de tarefas para abrir o Gerenciador de tarefas.
Clique com o botão direito do mouse no cabeçalho da tabela do Gerenciador de tarefas e ative a Memória JavaScript.
Essas duas colunas contêm informações diferentes sobre a forma como a página usa a memória:
- A coluna Consumo de memória representa a memória do SO. Os nós do DOM são armazenados na memória do SO. Se esse valor estiver aumentando, os nós do DOM estão sendo criados.
A coluna Memória JavaScript representa a pilha JS. Esta coluna contém dois valores. O valor que você deve olhar é o número ativo (o número entre parênteses). O número ativo representa quanta memória os objetos acessíveis na sua página estão usando. Se esse número estiver aumentando, novos objetos estão sendo criados ou os objetos existentes estão crescendo.
Visualizar vazamentos de memória com gravações de desempenho
Você também pode usar o painel Performance como outro ponto de partida para a investigação. O painel Performance ajuda a visualizar o uso de memória de uma página ao longo do tempo.
- Abra o painel Performance no DevTools.
- Marque a caixa de seleção Memória.
- Fazer uma gravação.
Para demonstrar as gravações de memória do Performance, considere este código:
var x = [];
function grow() {
for (var i = 0; i < 10000; i++) {
document.body.appendChild(document.createElement('div'));
}
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);
Todas as vezes que o botão referenciado no código é pressionado, dez mil nós div
são anexados
ao corpo do documento, e uma string de um milhão de caracteres x
é inserida na matriz x
.
A execução desse código gera uma gravação de Timeline semelhante à captura de tela abaixo:
Primeiro, uma explicação da interface do usuário. O gráfico HEAP no painel Overview (abaixo de NET) representa a pilha JS. Abaixo do painel Visão geral está o painel Contador. Aqui, o uso da memória é mostrado dividido por pilha do JS (como no gráfico HEAP no painel Overview), documentos, nós do DOM, listeners e memória da GPU. Desative uma caixa de seleção para ocultá-la do gráfico.
Agora, uma análise do código comparada com a captura de tela. Se você observar o contador de nós (o
gráfico verde), verá que ele corresponde exatamente ao código. A contagem de nós aumenta em
etapas discretas. Você pode presumir que cada aumento na contagem de nós é uma chamada para grow()
. O gráfico da pilha
JS (o gráfico azul) não é tão simples. De acordo com as práticas recomendadas, o primeiro fundo
é, na verdade, uma coleta de lixo forçada (resultante do pressionar do botão collect garbage). À medida
que a gravação avança, você pode notar que o tamanho da pilha JS aumenta. Isso é natural e esperado: o
código JavaScript está criando os nós do DOM a cada clique no botão e trabalhando muito quando
cria a string de um milhão de caracteres. O principal fator aqui é o fato de que a pilha JS encerra
mais alta do que começou (o "início" aqui é o ponto após a coleta de lixo forçada). No
mundo real, se você perceber esse padrão de tamanho crescente de pilha de JS ou nó, isso poderá
significar um vazamento de memória.
Descobrir vazamentos de memória da árvore do DOM desconectada com instantâneos de pilha
Um nó do DOM somente pode ser coletado como lixo quando não for referenciado pela árvore do DOM ou pelo código JavaScript da página. Um nó é considerado "desconectado" quando é removido da árvore do DOM, mas ainda tem referências no JavaScript. Nós do DOM desconectados são uma causa comum de vazamentos de memória. Esta seção ensina como usar os criadores de perfil de pilha do DevTools para identificar nós desconectados.
Confira um exemplo simples de nós de DOM desconectados.
var detachedTree;
function create() {
var ul = document.createElement('ul');
for (var i = 0; i < 10; i++) {
var li = document.createElement('li');
ul.appendChild(li);
}
detachedTree = ul;
}
document.getElementById('create').addEventListener('click', create);
Um clique no botão referenciado no código cria um nó ul
com dez filhos li
. Estes nós
são referenciados pelo código, mas não existem na árvore do DOM. Portanto, estão desconectados.
Os snapshots de pilha são uma forma de identificar nós desconectados. Como o nome indica, os snapshots de pilha mostram como a memória é distribuída entre os objetos JS e os nós DOM da página no momento do snapshot.
Para criar um snapshot, abra o DevTools e acesse o painel Memory, selecione o botão de opção Heap Snapshot e pressione o botão Take snapshot.
O processamento e o carregamento do instantâneo podem demorar algum tempo. Depois que terminar, selecione-o no painel à esquerda (chamado de Snapshots de pilha).
Digite Detached
na caixa de entrada Class filter para pesquisar árvores DOM desconectadas.
Expanda os quilates para investigar uma árvore desconectada.
Clique em um nó para investigar mais. No painel Objects, você pode conferir mais
informações sobre o código que está fazendo referência a ele. Por exemplo, na captura de tela a seguir, você pode ver
que a variável detachedTree
está referenciando o nó. Para corrigir esse vazamento de memória específico, estude o código que usa detachedTree
e garanta que ele remova a referência ao nó
quando não for mais necessário.
Identificar vazamentos de memória na pilha JS com Allocation Timelines
A Allocation Timeline é outra ferramenta que pode ajudar a rastrear vazamentos de memória na pilha JS.
Para demonstrar o Allocation Timeline, considere este código:
var x = [];
function grow() {
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);
Todas as vezes que o botão referenciado no código é acionado, uma string de um milhão de caracteres é
adicionada à matriz x
.
Para gravar uma Timeline de alocação, abra o DevTools, acesse o painel Memória, selecione o botão de opção Alocações na linha do tempo, pressione o botão
Gravar, realize a ação que você suspeita que está causando o vazamento de memória e pressione o botão Parar gravação quando terminar.Durante a gravação, observe se alguma barra azul aparece na linha do tempo de alocação, como na captura de tela a seguir.
Essas barras azuis representam novas alocações de memória. Essas novas alocações de memória são seus candidatos a ter vazamentos de memória. Você pode aumentar o zoom em uma barra e filtrar o painel Constructor para mostrar apenas os objetos que foram alocados durante o período especificado.
Expanda o objeto e clique no valor para conferir mais detalhes no painel Object. Por
exemplo, na captura de tela abaixo, ao conferir os detalhes do objeto recém-alocado,
você vai notar que ele foi alocado para a variável x
no escopo Window
.
Investigar a alocação de memória por função
Use o tipo de perfil Amostras de alocação no painel Memória para conferir a alocação de memória por função do JavaScript.
- Selecione o botão de opção Amostrar a alocação. Se houver um worker na página, você poderá selecioná-lo como o destino do perfil na janela Selecionar instância da VM JavaScript.
- Pressione o botão Start.
- Realize as ações na página que você quer investigar.
- Pressione o botão Parar quando terminar todas as ações.
O DevTools mostra a distribuição da alocação de memória por função. A visualização padrão é Heavy (Bottom Up), que mostra na parte de cima as funções que alocaram mais memória.
Identificar objetos retidos por referência JS
O perfil Elementos removidos mostra elementos removidos que persistem porque são referenciados pelo código JavaScript.
Grave um perfil de elementos separados para conferir os nós e a contagem de nós exatos do HTML.
Identificar coletas de lixo frequentes
Se uma página aparentar estar pausando com frequência, poderão estar ocorrendo problemas de coleta de lixo.
Você pode usar o Gerenciador de tarefas do Chrome ou os registros de memória da Timeline para identificar coletas de lixo frequentes. No Gerenciador de tarefas, os valores de Memória ou Memória JavaScript em aumento e diminuição frequentes representam coletas de lixo frequentes. Nas gravações da Timeline, gráficos de contagem de nós ou pilha JS frequentemente ascendentes e descendentes indicam coletas de lixo frequentes.
Depois de identificar o problema, você pode usar uma gravação da Allocation Timeline para descobrir onde a memória está sendo alocada e quais funções estão causando as alocações.