Gravar snapshots de heap

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

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

O criador de perfil de alocação heap mostra a distribuição de memória pelos objetos JavaScript da página e nós do DOM relacionados. Use-o para criar snapshots de heap do JS, analisar gráficos de memória, comparar snapshots e encontrar vazamentos de memória. Para mais informações, consulte Árvore de retenção de objetos.

Capturar um snapshot

Para criar um instantâneo de alocação heap:

  1. Na página em que você quer criar um perfil, abra o DevTools e navegue até o painel Memory.
  2. Selecione o tipo de criação de perfil radio_button_checked Snapshot de heap, selecione uma instância de VM JavaScript e clique em Criar snapshot.

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

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

O tamanho total dos objetos acessíveis.

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

Um instantâneo de heap de objetos Item espalhados.

Limpar snapshots

Para remover todos os snapshots, clique em block Clear all profiles:

Limpar todos os perfis.

Ver snapshots

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

View Conteúdo Finalidade
Resumo Objetos agrupados por nomes de construtor. Use-o para procurar objetos e seu uso de memória com base no tipo. Útil para rastrear vazamentos do DOM.
Comparação Diferenças entre dois snapshots. Use-o para comparar dois (ou mais) snapshots, antes e depois de uma operação. Confirme a presença e a causa de um vazamento de memória inspecionando o delta na memória liberada e na contagem de referência.
Contenção Conteúdo de heap Fornece uma visão melhor da estrutura do objeto e ajuda a analisar os objetos referenciados no namespace global (janela) para descobrir o que os mantém ativos. Use-a para analisar interdições e detalhar seus objetos em um nível baixo.
Estatísticas Gráfico de pizza da alocação de memória Confira os tamanhos reais das partes de memória alocadas para código, strings, matrizes JS, matrizes tipadas e objetos do sistema.

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

Visualização de resumo

Inicialmente, um resumo de heap é aberto na visualização Resumo, que lista os Construtores em uma coluna. Você pode expandir construtores para ver os objetos que eles instanciaram.

Visualização "Resumo" com um construtor expandido.

Para filtrar construtores irrelevantes, digite um nome que você queira 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:

  • Distance mostra a distância até a raiz usando o caminho simples mais curto de nós.
  • Shallow size mostra a soma dos tamanhos superficiais de todos os objetos criados por um determinado construtor. O tamanho superficial é o tamanho da memória retida pelo próprio objeto. Geralmente, matrizes e strings têm tamanhos superficiais maiores. Consulte também Tamanhos de objeto.
  • Tamanho retido mostra o tamanho máximo retido entre o mesmo conjunto de objetos. O tamanho retido é o tamanho da memória que pode ser liberado ao excluir um objeto e tornar os dependentes não acessí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 superficiais e retidos nas colunas correspondentes. O número após o caractere @ é o ID exclusivo do objeto. Ele permite comparar snapshots de heap por objeto.

Filtros de 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 ser capturado.
  • Objetos alocados entre os Snapshots 1 e 2: confira a diferença nos objetos entre o snapshot mais recente e o anterior. Cada snapshot novo adiciona um incremento desse filtro à lista suspensa.
  • Strings duplicadas: valores de string armazenados várias vezes na memória.
  • Objetos retidos por nós desconectados: objetos que são mantidos ativos porque um nó DOM desconectado faz referência a eles.
  • Objetos retidos pelo console do DevTools: objetos mantidos na memória porque foram avaliados ou interagiram com o 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;
  • Funções definidas no código.
  • Categorias especiais que não são baseadas em construtores.

Entradas de construtor.

(array)

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

Por exemplo, o conteúdo de objetos JavaScript Array é 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 estão listados na categoria (array).

(compiled code)

Esta categoria inclui dados internos que o V8 precisa para executar funções definidas pelo JavaScript ou WebAssembly. Cada função pode ser representada de várias maneiras, desde pequenas e lentas até grandes e rápidas.

O V8 gerencia automaticamente o uso da memória nesta categoria. Se uma função for executada muitas vezes, o V8 usará mais memória para essa função e, assim, poderá executá-la com mais rapidez. Se uma função não é executada por algum tempo, o V8 pode limpar os dados internos dela.

(concatenated string)

Quando V8 concatena duas strings, como no operador 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 em 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. Da perspectiva do código JavaScript, são apenas strings normais e se comportam como qualquer outra string.

InternalNode

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

Para ver os nomes das classes C++, use o Chrome for Testing e faça o seguinte:

  1. Abra o DevTools e ative as configurações Configurações > Experimentos > check_box Mostrar opção para expor elementos internos em snapshots de heap.
  2. Abra o painel Memória, selecione radio_button_checked Snapshot do heap e ative radio_button_checked Expor componentes internos (inclui detalhes adicionais específicos da implementação).
  3. Reproduza o problema que fez com que o InternalNode retinha muita memória.
  4. Tirar um instantâneo da pilha. 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 e na mesma ordem possam ser representados de forma eficiente. A categoria contém essas classes ocultas, chamadas system / Map (não relacionadas ao JavaScript Map), e os dados relacionados.

(sliced string)

Quando o V8 precisa usar uma substring, como quando o código JavaScript chama String.prototype.substring(), o V8 pode optar por alocar um objeto de string cortada 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.

Da perspectiva do código JavaScript, são apenas strings normais e se comportam como qualquer outra string. Se uma string dividida estiver retendo muita memória, é possível que o programa tenha acionado o Problema 2869 e se beneficie de etapas deliberadas para "nivelar" essa string.

system / Context

Objetos internos do tipo system / Context contêm variáveis locais de um closure, um escopo JavaScript que uma função aninhada pode acessar.

Cada instância de função contém um ponteiro interno para a Context em que ela é executada para poder acessar essas variáveis. Embora os objetos Context não fiquem 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 maneira mais significativa.

Visualização de comparação

A visualização Comparação permite encontrar objetos vazados comparando vários snapshots entre si. Por exemplo, realizar uma ação e reverter a ação, como abrir e fechar um documento, não deve deixar objetos extras para trás.

Para verificar se uma determinada operação não está gerando vazamentos:

  1. Tire um snapshot de heap antes de executar uma operação.
  2. Execute uma operação. Ou seja, interaja com a página de alguma forma que possa estar causando um vazamento.
  3. Realize uma operação reversa. Ou seja, faça a interação oposta e repita-a algumas vezes.
  4. Tire um segundo snapshot de heap e altere a visualização para Comparação, comparando-o a Snapshot 1.

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

Comparação com o Snapshot 1.

Visualização de contenção

A visualização Contenção é uma "visão geral" da estrutura dos objetos do seu aplicativo. Ela permite que você veja os fechamentos de funções, observe os objetos internos da VM que, juntos, compõem seus objetos JavaScript e entenda quanta memória seu 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 GC. Raízes de GC usadas pelo coletor de lixo da VM. As raízes de GC podem consistir em mapas de objetos integrados, tabelas de símbolos, pilhas de linhas de execução de VM, caches de compilação, escopos de identificadores e identificadores globais.
  • Objetos nativos. Os objetos do navegador são inseridos na máquina virtual JavaScript para permitir a automação, por exemplo, de nós de DOM e regras de CSS.

A visualização Containment.

A seção Retenção

A seção Retainers na parte inferior do painel Memory mostra objetos que apontam para o objeto selecionado na visualização. O painel Memory atualiza a seção Retainers quando você seleciona objetos diferentes em qualquer uma das visualizações, exceto Statistics.

A seção Retenção.

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

Ignorar retenções

Você pode ocultar as retenções para descobrir se qualquer outro objeto retém o selecionado. Com essa opção, não é necessário remover essa retenção do código antes de fazer o instantâneo da pilha novamente.

A opção "Ignorar esta retenção" no menu suspenso.

Para ocultar uma retenção, clique com o botão direito do mouse e selecione Ignorar esta retenção. As retenções ignoradas são marcadas como ignored na coluna Distance. Para parar de ignorar todas as retenções, clique em playlist_remove Restaurar retenções ignoradas na barra de ações na parte superior.

Encontrar um objeto específico

Para encontrar um objeto na heap coletada, pesquise usando Ctrl + F e insira o ID do objeto.

Nomear funções para distinguir interdições

É muito útil nomear as funções para você poder distinguir fechamentos no instantâneo.

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 uma interdição.

Descobrir vazamentos do DOM

O criador de perfil de alocação heap pode refletir dependências bidirecionais entre objetos nativos do navegador (nós DOM e regras CSS) e objetos JavaScript. Isso ajuda a descobrir vazamentos que não existem devido a subárvores do DOM desconectadas esquecidas que flutuam por aí.

Os vazamentos no DOM podem ser maiores do que você imagina. Considere este exemplo. Quando o lixo da #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 recursivamente até #tree. Portanto, somente quando leafRef for anulado, a árvore toda em #tree será candidata a GC.

Subárvores do DOM