Aprenda a usar o Chrome e o DevTools para encontrar problemas de memória que afetam o desempenho da página, incluindo vazamentos, inchaço e coletas de lixo frequentes.
Resumo
- Descubra quanta memória sua página está usando com o Gerenciador de tarefas do Chrome.
- Visualize o uso da memória ao longo do tempo com as gravações da linha do tempo.
- Identifique árvores DOM separadas (uma causa comum de vazamentos de memória) com Heap Snapshots.
- Descubra quando uma nova memória está sendo alocada na pilha do JS com as gravações da linha do tempo de alocação.
- Identifique elementos separados retidos pela referência do JavaScript.
Visão geral
No espírito do modelo de performance RAIL, o foco dos seus esforços de performance deve ser os usuários.
Os problemas de memória são importantes porque muitas vezes são percebidos pelos usuários. Os usuários podem perceber problemas de memória das seguintes maneiras:
- A performance de uma página piora progressivamente ao longo do tempo. Isso pode ser um sintoma de um 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 ao longo do tempo.
- A performance de uma página é consistentemente ruim. Isso pode ser um sintoma de inchaço de memória. O inchaço de memória ocorre quando uma página usa mais memória do que o necessário para uma velocidade ideal.
- O desempenho de uma página está atrasado ou parece ser pausado com frequência. Isso pode ser um sintoma de coleções de lixo frequentes. A coleta de lixo é quando o navegador recupera a memória. O navegador decide quando isso acontece. Durante as coleções, toda a execução do script é pausada. Portanto, se o navegador faz muita coleta de lixo, a execução do script será pausada com frequência.
Expansão de memória: o que é "demais"?
Um vazamento de memória é fácil de definir. Se um site estiver usando cada vez mais memória, significa que há um vazamento. Mas o inchaço de memória é um pouco mais difícil de identificar. O que é considerado "uso excessivo de memória"?
Não há números exatos aqui, porque diferentes dispositivos e navegadores têm recursos diferentes. A mesma página que funciona sem problemas em um smartphone de última geração pode apresentar falhas em um smartphone de baixo custo.
O importante aqui é usar o modelo RAIL e se concentrar nos usuários. Descubra quais dispositivos são populares entre seus usuários e teste a página nesses dispositivos. Se a experiência for consistentemente ruim, a página pode estar excedendo os recursos de memória desses dispositivos.
Monitorar o uso da 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 mostram coisas diferentes sobre como a página está usando a memória:
- A coluna Consumo de memória representa a memória do SO. Os nós DOM são armazenados na memória do SO. Se esse valor estiver aumentando, os nós DOM estão sendo criados.
A coluna Memória JavaScript representa o heap do JS. Essa coluna contém dois valores. O valor de que você precisa é o número em tempo real (o número entre parênteses). O número em tempo real representa a quantidade de memória que os objetos acessíveis na 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 na 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 o seguinte 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);
Toda vez 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 no array x
.
A execução desse código produz uma gravação da linha do tempo como a captura de tela abaixo:
Primeiro, uma explicação da interface do usuário. O gráfico HEAP no painel Overview (abaixo de NET) representa o heap do JS. Abaixo do painel Visão geral, está o painel Contador. Aqui você pode ver o uso da memória dividido por heap do JS (o mesmo que o gráfico HEAP no painel Overview), documentos, nós DOM, listeners e memória da GPU. Desativar uma caixa de seleção oculta o item do gráfico.
Agora, uma análise do código em comparação com a captura de tela. Se você olhar para o contador de nós (o
gráfico verde), vai notar que ele corresponde ao código. A contagem de nós aumenta em
etapas discretas. É possível presumir que cada aumento na contagem de nós é uma chamada para grow()
. O gráfico de pilha
do JS (o gráfico azul) não é tão simples. De acordo com as práticas recomendadas, a primeira queda
é uma coleta de lixo forçada (feita pressionando o botão coletar lixo). À medida
que a gravação avança, é possível notar que o tamanho do heap do JS aumenta. Isso é natural e esperado: o
código JavaScript está criando os nós DOM em cada clique no botão e fazendo muito trabalho ao
criar a string de um milhão de caracteres. O ponto principal aqui é o fato de que a pilha do JS termina
mais alta do que começou (o "início" aqui é o ponto após a coleta de lixo forçada). Na
vida real, se você notar esse padrão de aumento do tamanho da pilha do JS ou do nó, isso
pode significar um vazamento de memória.
Descubra vazamentos de memória da árvore do DOM desconectados com os snapshots do Heap
Um nó DOM só pode ser coletado como lixo quando não há referências a ele na árvore DOM da página ou no código JavaScript. Um nó é considerado "desconectado" quando é removido da árvore DOM, mas alguns JavaScripts ainda o referenciam. Nós DOM removidos são uma causa comum de vazamentos de memória. Esta seção ensina como usar os profilers de heap do DevTools para identificar nós separados.
Confira um exemplo simples de nós DOM separados.
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);
Clicar no botão referenciado no código cria um nó ul
com dez filhos li
. Esses nós
são referenciados pelo código, mas não existem na árvore DOM. Por isso, eles são separados.
Os snapshots de heap são uma maneira de identificar nós desconectados. Como o nome indica, os snapshots de heap 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 as Ferramentas do desenvolvedor 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 snapshot podem levar algum tempo. Quando terminar, selecione-o no painel à esquerda (chamado Snapshots de heap).
Digite Detached
na caixa de entrada Filtro de classe para pesquisar árvores DOM separadas.
Abra os carats 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 faz referência a ele. Por exemplo, na captura de tela a seguir, é possível ver
que a variável detachedTree
faz referência ao nó. Para corrigir esse vazamento de memória específico, estude o código que usa detachedTree
e verifique se ele remove a referência ao nó
quando não for mais necessário.
Identificar vazamentos de memória de heap do JS com as Linhas do tempo de alocação
A Linha do tempo de alocação é outra ferramenta que pode ajudar a rastrear vazamentos de memória no heap do JS.
Para demonstrar a linha do tempo de alocação, considere o seguinte código:
var x = [];
function grow() {
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);
Toda vez que o botão referenciado no código é enviado, uma string de um milhão de caracteres é
adicionada à matriz x
.
Para gravar uma linha do tempo de alocação, abra as Ferramentas do desenvolvedor, 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 há barras azuis 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 os candidatos para vazamentos de memória. É possível dar zoom em uma barra para filtrar o painel Constructor e mostrar apenas os objetos que foram alocados durante o período especificado.
Abra o objeto e clique no valor dele para conferir mais detalhes no painel Object. Por
exemplo, na captura de tela abaixo, ao conferir os detalhes do objeto que foi alocado recentemente,
você pode ver 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 JavaScript.
- Selecione o botão de opção Alocação de amostragem. 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.
As Ferramentas do desenvolvedor mostram um detalhamento da alocação de memória por função. A visualização padrão é Heavy (Bottom Up), que mostra as funções que alocaram mais memória na parte de cima.
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.
Detectar coletas de lixo frequentes
Se a página parecer pausar com frequência, talvez você tenha problemas de coleta de lixo.
Você pode usar o Gerenciador de tarefas do Chrome ou as gravações de memória da Linha do tempo para identificar coletas de lixo frequentes. No Gerenciador de tarefas, os valores de Memória ou Memória JavaScript que aumentam e diminuem com frequência representam coletas de lixo frequentes. Nas gravações da linha do tempo, os gráficos de contagem de nós ou de pilha JS em ascensão e queda indicam coletas de lixo frequentes.
Depois de identificar o problema, use uma gravação da linha do tempo de alocação para descobrir onde a memória está sendo alocada e quais funções estão causando as alocações.