Corrigir problemas de memória

Kayce Basques
Kayce Basques

Aprenda a usar o Chrome e o DevTools para encontrar problemas de memória que afetam o desempenho da página, incluindo vazamentos de memória, sobrecarga de memória e coletas de lixo frequentes.

Resumo

  • Descubra quanta memória sua página está usando no momento com o Gerenciador de tarefas do Chrome.
  • Confira o uso da memória ao longo do tempo com os registros da Linha do tempo.
  • Identifique árvores do DOM desconectadas (uma causa comum de vazamentos de memória) com instantâneos de pilha.
  • Descubra quando nova memória é alocada a sua heap JS com os registros da Allocation Linha do tempo.

Visão geral

De acordo com o modelo de desempenho RAIL, o foco das suas iniciativas de desempenho precisam ser os usuários.

Os problemas de memória são importantes porque muitas vezes são perceptíveis 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 cada vez mais memória ao longo do tempo.
  • O desempenho de uma página é consistentemente ruim. Isso é possivelmente um sintoma de ocupação excessiva da memória. A ocupação de memória ocorre quando uma página usa mais memória do que o necessário para ter a velocidade ideal.
  • O desempenho de uma página atrasa ou parece pausar com frequência. Isso é um sintoma de coletas de lixo frequentes. A coleta de lixo ocorre quando o navegador recupera a memória. O navegador decide quando isso acontece. Durante as coletas, toda a execução do script é pausada. Portanto, se o navegador estiver coletando muito lixo, a execução do script também será pausada.

Excesso de memória: quanto é "demais"?

É fácil definir um vazamento de memória. Se um site estiver usando cada vez mais memória, é porque há um vazamento. Mas a ocupação excessiva da memória é um pouco mais difícil de detectar. O que se qualifica como "uso excessivo de memória"?

Não há números fixos aqui, porque dispositivos e navegadores diferentes têm recursos diferentes. A mesma página que é executada suavemente em um smartphone sofisticado pode falhar em um smartphone mais simples.

A chave aqui é usar o modelo RAIL e focar nos seus usuários. Descubra quais dispositivos são mais usados pelos 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 na investigação de problemas de memória. O gerenciador de tarefas é um monitor em tempo real que informa quanta memória uma página está usando no momento.

  1. Pressione Shift+Esc ou acesse o menu principal do Chrome e selecione Mais ferramentas > Gerenciador de tarefas para abrir o gerenciador de tarefas.

    Como abrir o Gerenciador de tarefas

  2. Clique com o botão direito do mouse no cabeçalho da tabela do gerenciador de tarefas e ative a memória JavaScript.

    Como ativar a memória do JS

Essas duas colunas contêm informações diferentes sobre como sua página está usando a memória:

  • A coluna Memória representa a memória nativa. Os nós DOM são armazenados na memória nativa. Se esse valor está aumentando, os nós do DOM estão sendo criados.
  • A coluna Memória JavaScript representa a heap JS. Essa coluna contém dois valores. O valor em que você está interessado é 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.

Visualize vazamentos de memória com os registros de desempenho

Você também pode usar o painel "Desempenho" como outro ponto de partida na sua investigação. O Painel de desempenho ajuda a visualizar o uso de memória de uma página ao longo do tempo.

  1. Abra o painel Desempenho no DevTools.
  2. Marque a caixa de seleção Memória.
  3. Fazer uma gravação.

Para demonstrar os registros de memória de performance, considere o código abaixo:

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, 10 mil nós div são anexados ao corpo do documento, e uma string de um milhão de caracteres x é enviada para a matriz x. A execução desse código produz uma gravação da linha do tempo como a seguinte captura de tela:

exemplo de crescimento simples

Primeiro, uma explicação sobre a interface do usuário. O gráfico HEAP no painel Visão geral (abaixo de NET) representa o heap JS. Abaixo do painel Visão geral fica o painel Contador. Aqui é possível ver o uso da memória detalhado por heap JS (igual ao gráfico HEAP no painel Visão geral), documentos, nós do DOM, listeners e memória da GPU. Quando você desativa uma caixa de seleção, ela fica oculta no gráfico.

Agora, uma análise do código comparada com a captura de tela. Ao analisar o contador de nós (o gráfico verde), você verá que ele corresponde perfeitamente ao código. A contagem de nós aumenta em etapas distintas. É possível presumir que cada aumento na contagem de nós seja uma chamada para grow(). O gráfico de heap JS (o gráfico azul) não é tão simples. De acordo com as práticas recomendadas, a primeira queda é, na verdade, uma coleta de lixo forçada (alcançada pressionando o botão collect lixeira). Conforme a gravação avança, o tamanho do heap do JS aumenta. Isso é natural e esperado: o código JavaScript está criando os nós DOM em cada clique em cada botão e faz muito trabalho quando cria a string de um milhão de caracteres. O principal aqui é o fato de que o heap do JS termina mais cedo do que começou (o "início" é o ponto após a coleta de lixo forçada). No mundo real, se você perceber esse padrão de aumento do tamanho do heap ou do nó do JS, isso pode significar um vazamento de memória.

Descubra vazamentos de memória da árvore do DOM desconectada com instantâneos de pilha

Um nó do DOM só pode ser coletado como lixo quando não houver referências a ele na árvore DOM ou no código JavaScript da página. Um nó é considerado "desanexado" quando é removido da árvore do DOM, mas algum JavaScript ainda faz referência a ele. 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 heap do DevTools para identificar nós desconectados.

Veja a seguir um exemplo simples de nós do 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);

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 do DOM. Por isso, eles estão desconectados.

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 do 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.

criar instantâneo de alocação heap

O processamento e o carregamento do snapshot podem levar algum tempo. Quando terminar, selecione-o no painel à esquerda (chamado HEAP SNAPSHOTS).

Digite Detached na caixa de texto Filtro de classe para pesquisar árvores do DOM desconectadas.

filtragem de nós desconectados

Expanda os quilates para investigar uma árvore desconectada.

investigando árvore independente

Os nós destacados em amarelo fazem referência direta a eles no código JavaScript. Os nós destacados em vermelho não têm referências diretas. Eles só estão vivos porque fazem parte da árvore dos nós amarelos. Em geral, convém se concentrar nos nós amarelos. Corrija o código para que o nó amarelo não fique ativo por mais tempo do que o necessário. Além disso, você se livrará dos nós vermelhos que fazem parte da árvore de nós amarelos.

Clique em um nó amarelo para investigar melhor. No painel Objetos, você pode conferir mais informações sobre o código que faz referência a ele. Por exemplo, na captura de tela abaixo, você pode ver que a variável detachedTree está referenciando o nó. Para corrigir esse vazamento de memória específico, você estuda o código que usa detachedTree e garante que ele remova a referência ao nó quando não for mais necessária.

investigar um nó amarelo

Identifique vazamentos de memória de heap JS com as linhas do tempo de alocação

A Allocation Linha do tempo é outra ferramenta que pode ajudá-lo a rastrear vazamentos de memória na pilha 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);

Sempre que o botão referenciado no código for pressionado, uma string de um milhão de caracteres será adicionada à matriz x.

Para gravar uma linha do tempo de alocação, abra o DevTools, acesse o painel Profiles, selecione o botão de opção Record Allocation Linha do tempo, pressione o botão Start, execute a ação que você suspeita que esteja causando o vazamento de memória e pressione o botão Parar gravação (botão &quot;Parar gravação&quot;) quando terminar.

Durante a gravação, observe se alguma barra azul aparece na Allocation Linha do tempo, como na captura de tela abaixo.

novas alocações

Essas barras azuis representam novas alocações de memória. Essas novas alocações de memória são candidatas a vazamentos de memória. É possível aplicar zoom em uma barra para filtrar o painel Construtor e mostrar apenas os objetos que foram alocados durante o período especificado.

linha do tempo de alocação com zoom ampliado

Expanda o objeto e clique no valor dele para ver mais detalhes sobre ele no painel Objeto. Por exemplo, na captura de tela abaixo, ao visualizar os detalhes do objeto recém-alocado, é possível ver que ele foi alocado para a variável x no escopo Window.

detalhes do objeto

Investigar a alocação de memória por função

Use o tipo Amostragem de alocação no painel Memória para visualizar a alocação de memória por função do JavaScript.

Criador de perfil de alocação de registro

  1. Selecione o botão de opção Amostragem de alocação. Se houver um worker na página, ele poderá ser selecionado como o destino da criação de perfil usando o menu suspenso ao lado do botão Start.
  2. Pressione o botão Start.
  3. Execute as ações na página que você quer investigar.
  4. Pressione o botão Parar quando tiver concluído todas as ações.

O DevTools mostra um detalhamento da alocação de memória por função. A visualização padrão é Strong (Bottom Up), que mostra as funções que alocaram mais memória na parte de cima.

Perfil de alocação

Identificar coletas de lixo frequentes

Se a página parece pausar com frequência, é possível que você tenha problemas com a coleta de lixo.

Você pode usar o gerenciador de tarefas do Chrome ou os registros de memória da linha do tempo para identificar coletas de lixo frequentes. No gerenciador de tarefas, aumentar e diminuir frequentemente os valores de Memória ou Memória JavaScript representam coletas de lixo frequentes. Nas gravações da Linha do tempo, gráficos que aumentam e diminuem com frequência de heap JS ou de contagem de nós 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.