Gravar snapshots de heap

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

Saiba como registrar resumos de pilha com Memória > Perfis > Resumo de pilha e encontrar vazamentos de memória.

O criador de perfil de pilha mostra a distribuição de memória pelos objetos JavaScript da página e os nós DOM relacionados. Use-o para fazer capturas de tela de pilha do JS, analisar gráficos de memória, comparar instantâneos e encontrar vazamentos de memória. Para mais informações, consulte Árvore de retenção de objetos.

Capturar um instantâneo

Para criar um snapshot de alocação heap:

  1. Em uma página que você quer analisar, abra o DevTools e navegue até o painel Memória.
  2. Selecione o tipo de análise de perfil Snapshot de pilha, selecione uma instância de VM JavaScript e clique em Tirar snapshot.

Um tipo de criação de perfil selecionado e uma instância de VM JavaScript.

Quando o painel Memória carrega e analisa o instantâneo, ele mostra o tamanho total dos objetos JavaScript acessíveis abaixo do título do instantâneo na seção INSTANTES DE HEAP.

O tamanho total dos objetos acessíveis.

Os instantâneos mostram apenas os objetos do gráfico de memória que podem ser acessados pelo objeto global. A captura de um instantâneo sempre começa com a coleta de lixo.

Um instantâneo de pilha de objetos de item espalhados.

Limpar snapshots

Para remover todos os instantâneos, clique em Limpar todos os perfis:

Limpar todos os perfis.

Ver snapshots

Para inspecionar instantâneos de diferentes perspectivas para finalidades diferentes, selecione uma das visualizações no menu suspenso na parte de cima:

Ver Conteúdo Finalidade
Resumo Objetos agrupados por nomes de construtor. Use-o para buscar objetos e o uso de memória com base no tipo. Útil para rastrear vazamentos do DOM.
Comparação Diferenças entre dois snapshots. Use-a para comparar dois (ou mais) resumos, antes e depois de uma operação. Confirme a presença e a causa de um vazamento de memória inspecionando a diferença entre a memória liberada e a contagem de referência.
Contenção Conteúdo do heap Ela fornece uma melhor visualização da estrutura do objeto e ajuda a analisar objetos referenciados no espaço de nome global (janela) para descobrir o que está mantendo-os ativos. Use-a para analisar fechamentos e examinar detalhadamente seus objetos.
Estatísticas Gráfico de pizza de alocação de memória Confira os tamanhos relativos das partes de memória alocadas para código, strings, matrizes JS, matrizes digitadas e objetos do sistema.

A visualização "Resumo" selecionada no menu suspenso na parte de cima.

Visualização resumida

Inicialmente, um instantâneo da pilha é aberto na visualização Summary, que lista Constructors em uma coluna. É possível expandir os construtores para conferir os objetos que eles instanciaram.

A visualização "Summary" com um construtor expandido.

Para filtrar construtores irrelevantes, digite um nome que você quer inspecionar no Filtro de classe na parte de cima da visualização Resumo.

Os números ao lado dos nomes do construtor indicam o número total de objetos criados com o construtor. A visualização Resumo também mostra as seguintes colunas:

  • Distância mostra a distância até a raiz usando o caminho de nós simples mais curto.
  • Tamanho superficial mostra a soma dos tamanhos superficiais de todos os objetos criados por um determinado construtor. O tamanho superficial é o tamanho da memória detido pelo próprio objeto. Em geral, matrizes e strings têm tamanhos superficiais maiores. Consulte também Tamanhos de objeto.
  • Tamanho retido mostra o tamanho retido máximo dentre o mesmo conjunto de objetos. O tamanho retido é a quantidade de memória que pode ser liberada ao excluir um objeto e tornar seus dependentes inacessíveis. Consulte também Tamanhos de objeto.

Quando você expande um construtor, a visualização Resumo mostra todas as instâncias dele. Cada instância recebe um detalhamento dos tamanhos superficial e retido nas colunas correspondentes. O número depois do caractere @ é o ID exclusivo do objeto. Ele permite comparar snapshots de pilha por objeto.

Filtros do construtor

A visualização Resumo permite filtrar construtores com base em casos comuns de uso ineficiente de memória.

Para usar esses filtros, selecione uma das seguintes opções no menu suspenso à direita na barra de ações:

  • Todos os objetos: todos os objetos capturados pelo snapshot atual. Definido por padrão.
  • Objetos alocados antes do snapshot 1: objetos que foram criados e permaneceram na memória antes do primeiro snapshot.
  • Objetos alocados entre os snapshots 1 e 2: confira a diferença nos objetos entre o snapshot mais recente e o anterior. Cada novo snapshot adiciona um incremento desse filtro à lista suspensa.
  • Strings duplicadas: valores de string armazenados várias vezes na memória.
  • Objetos retidos por nós removidos: objetos que são mantidos porque um nó DOM removido os referencia.
  • Objetos retidos pelo console do DevTools: objetos mantidos na memória porque foram avaliados ou com os quais houve interação pelo console do DevTools.

Entradas especiais no resumo

Além de agrupar por construtores, a visualização Resumo também agrupa objetos por:

  • Funções integradas, como Array ou Object.
  • Elementos HTML agrupados por tags, por exemplo, <div>, <a>, <img> e outros.
  • Funções que você definiu no código.
  • Categorias especiais que não são baseadas em construtores.

Entradas do construtor.

(array)

Essa categoria inclui vários objetos semelhantes a matrizes internos que não correspondem diretamente a objetos visíveis no JavaScript.

Por exemplo, o conteúdo dos objetos Array do JavaScript é armazenado em um objeto interno secundário chamado (object elements)[] para facilitar o redimensionamento. Da mesma forma, as propriedades nomeadas em objetos JavaScript geralmente são armazenadas em objetos internos secundários chamados (object properties)[], que também são listados na categoria (array).

(compiled code)

Essa categoria inclui dados internos necessários para que o V8 execute funções definidas pelo JavaScript ou pelo WebAssembly. Cada função pode ser representada de várias maneiras, de pequena e lenta a grande e rápida.

O V8 gerencia automaticamente o uso de memória nessa categoria. Se uma função for executada muitas vezes, o V8 vai usar mais memória para que ela seja executada mais rapidamente. Se uma função não for executada por um tempo, o V8 poderá limpar os dados internos dela.

(concatenated string)

Quando o V8 concatena duas strings, como com o operador + do JavaScript, ele pode representar o resultado internamente como uma "string concatenada", também conhecida como estrutura de dados Rope.

Em vez de copiar todos os caracteres das duas strings de origem para uma nova string, o V8 aloca um pequeno objeto com campos internos chamados first e second, que apontam para as duas strings de origem. Isso permite que o V8 economize tempo e memória. Do ponto de vista do código JavaScript, elas são apenas strings normais e se comportam como qualquer outra string.

InternalNode

Essa categoria representa objetos alocados fora do V8, como objetos C++ definidos pelo Blink.

Para conferir os nomes de classe em C++, use o Chrome para testes e faça o seguinte:

  1. Abra o DevTools e ative a opção Settings > Experiments > Show option to expose internals in heap snapshots.
  2. Abra o painel Memória, selecione Snapshot da pilha e ative Expos internos (inclui detalhes específicos da implementação).
  3. Reproduzir o problema que fez com que o InternalNode retivesse muita memória.
  4. Crie um snapshot de alocação heap. Neste snapshot, os objetos têm nomes de classe C++ em vez de InternalNode.
(object shape)

Conforme descrito em Propriedades rápidas no V8, o V8 rastreia classes ocultas (ou formas) para que vários objetos com as mesmas propriedades na mesma ordem possam ser representados de maneira eficiente. Essa categoria contém essas classes ocultas, chamadas system / Map (não relacionadas ao JavaScript Map), e dados relacionados.

(sliced string)

Quando o V8 precisa usar uma substring, como quando o código JavaScript chama String.prototype.substring(), o V8 pode escolher alocar um objeto string dividida em vez de copiar todos os caracteres relevantes da string original. Esse novo objeto contém um ponteiro para a string original e descreve qual intervalo de caracteres da string original usar.

Do ponto de vista do código JavaScript, elas são apenas strings normais e se comportam como qualquer outra string. Se uma string dividida estiver retendo muita memória, o programa pode ter acionado a Issue 2869 e pode se beneficiar de medidas deliberadas para "achatar" a string dividida.

system / Context

Os objetos internos do tipo system / Context contêm variáveis locais de um fechamento: um escopo JavaScript que uma função aninhada pode acessar.

Cada instância de função contém um ponteiro interno para o Context em que ela é executada para acessar essas variáveis. Embora os objetos Context não sejam diretamente visíveis no JavaScript, você tem controle direto sobre eles.

(system)

Essa categoria contém vários objetos internos que ainda não foram categorizados de uma maneira mais significativa.

Visualização de comparação

A vista Comparison permite encontrar objetos com vazamento comparando vários snapshots entre si. Por exemplo, realizar uma ação e revertê-la, como abrir e fechar um documento, não deve deixar objetos extras.

Para verificar se uma determinada operação não cria vazamentos:

  1. Faça um resumo do heap antes de realizar uma operação.
  2. Execute uma operação. Ou seja, interagir com uma página de uma forma que você acha que pode estar causando um vazamento.
  3. Executar uma operação reversa. Ou seja, faça a interação oposta e repita algumas vezes.
  4. Tire um segundo snapshot da pilha e mude a visualização para Comparison, comparando-o com Snapshot 1.

A visualização Comparison mostra a diferença entre dois instantâneos. Ao expandir uma entrada total, são mostradas as instâncias de objeto adicionadas e excluídas:

Comparação com o Snapshot 1.

Visualização de contenção

A visualização Containment é uma "visualização geral" da estrutura de objetos do seu app. Ela permite observar dentro de fechamentos de função, observar objetos internos da VM que, juntos, compõem os objetos JavaScript e compreender quanta memória o aplicativo usa em um nível muito baixo.

A visualização oferece vários pontos de entrada:

  • Objetos DOMWindow. Objetos globais para código JavaScript.
  • Raízes do GC. Raízes GC usadas pelo coletor de lixo da VM. As raízes GC podem consistir em mapas de objeto integrados, tabelas de símbolos, pilhas de encadeamento da VM, caches de compilação, escopos de identificador e identificadores globais.
  • Objetos nativos. Objetos do navegador "enviados" para a máquina virtual do JavaScript para permitir a automação, por exemplo, nós do DOM e regras CSS.

Visualização &quot;Containment&quot;.

Seção "Retenções"

A seção Retainers na parte de baixo do painel Memory mostra objetos que apontam para o objeto selecionado na visualização. O painel Memória atualiza a seção Retenção quando você seleciona objetos diferentes em qualquer uma das visualizações, exceto Estatísticas.

Seção &quot;Retenções&quot;.

Neste exemplo, a string selecionada é retida pela propriedade x de uma instância Item.

Ignorar retenções

É possível ocultar os retentores para descobrir se outros objetos mantêm o selecionado. Com essa opção, você não precisa remover o retentor do código e fazer o snapshot da pilha novamente.

A opção &quot;Ignorar este retentor&quot; no menu suspenso.

Para ocultar um retentor, clique com o botão direito do mouse e selecione Ignorar este retentor. Os retentores ignorados são marcados como ignored na coluna Distância. Para parar de ignorar todos os retentores, clique em Restore ignored retainers na barra de ações na parte de cima.

Encontrar um objeto específico

Para encontrar um objeto na pilha coletada, você pode pesquisar usando Ctrl + F e inserir o ID do objeto.

Nomear funções para distinguir fechamentos

É muito útil nomear as funções para permitir distinguir facilmente os fechamentos no resumo.

Por exemplo, o código a seguir não usa funções nomeadas:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function() { // this is NOT a named function
    return largeStr;
  };

  return lC;
}

Enquanto este exemplo faz:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function lC() { // this IS a named function
    return largeStr;
  };

  return lC;
}

Função nomeada em um fechamento.

Descobrir vazamentos do DOM

O criador de perfis de pilha tem a capacidade de refletir dependências bidirecionais entre objetos nativos do navegador (nós do DOM e regras CSS) e objetos JavaScript. Isso ajuda a descobrir vazamentos invisíveis que ocorrem devido a subárvores do DOM desconectadas que foram esquecidas e ficaram flutuando por aí.

Os vazamentos no DOM podem ser maiores do que você pensa. Veja o exemplo a seguir. Quando o lixo do #tree é coletado?

  var select = document.querySelector;
  var treeRef = select("#tree");
  var leafRef = select("#leaf");
  var body = select("body");

  body.removeChild(treeRef);

  //#tree can't be GC yet due to treeRef
  treeRef = null;

  //#tree can't be GC yet due to indirect
  //reference from leafRef

  leafRef = null;
  //#NOW #tree can be garbage collected

#leaf mantém uma referência ao pai (parentNode) e de forma recursiva até #tree. Portanto, somente quando leafRef é anulado, a árvore inteira em #tree é um candidato a GC.

Subárvores do DOM