Don't forget the Chrome Dev Summit, starting Monday at 10:00am (Pacific) and streaming live on YouTube. Schedule.

Como registrar instantâneos de pilha

Saiba como registrar resumos de pilha com o criador de perfil de pilha do Chrome DevTools e encontrar vazamentos de memória.

O criador de perfil de pilha do Chrome DevTools mostra a distribuição de memória pelos objetos JavaScript e nós DOM relacionados em uma página (consulte também Árvore de retenção de objetos). Use-o para capturar snapshots de pilha do JS, analisar gráficos de memória, comparar instantâneos e encontrar vazamentos de memória.

Capturar um instantâneo

No painel Profiles, escolha Take Heap Snapshot e clique em Start ou pressione Cmd + E ou Ctrl + E:

Selecionar o tipo de criação do perfil

Os instantâneos são armazenados inicialmente na memória de processo do renderizador. Eles são transferidos ao DevTools sob demanda quando você clica no ícone do instantâneo para visualizá-lo.

Após o carregamento e a análise do instantâneo no DevTools, o número abaixo do título do instantâneo é exibido, mostrando o tamanho dotal dos objetos JavaScript acessíveis:

Tamanho total dos objetos acessíveis

Observação: somente os objetos acessíveis são incluídos nos instantâneos. Além disso, a captura de um instantâneo sempre começa com uma coleta de lixo.

Limpar instantâneos

Remova instantâneos (do DevTools e da memória do renderizador) pressionando o ícone Clear all profiles:

Remover instantâneos

O fechamento da janela do DevTools não excluir os perfis da memória do renderizador. Ao reabrir o DevTools, todos os resumos capturadas anteriormente reaparecerão na lista de resumos.

Exemplo: Experimente este exemplo de objetos dispersos e gere um perfil usando o Criador de perfil de pilha. Você deve ver um número de alocações de itens (objetos).

Visualizar instantâneos

Visualize instantâneos de diferentes perspectivas para tarefas diferentes.

A visualização Summary mostra objetos agrupados pelo nome do construtor. Use-o para buscar objetos (e seu uso de memória) com base no tipo agrupado pelo nome do construtor. Ela é particularmente útil para localizar vazamentos do DOM.

A visualização Comparison exibe as diferenças entre dois instantâneos. Use-a para comparar dois (ou mais) resumos da memória de antes e depois de uma operação. A inspeção da diferença entre a memória liberada e a contagem de referência permite confirmar a presença e a causa de um vazamento de memória.

A visualização Containment permite explorar o conteúdo da pilha. Ela fornece uma melhor visualização da estrutura do objeto, ajudando 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.

A visualização Dominators mostra a árvore de dominadores e pode ser útil para encontrar pontos de acumulação. Essa visualização ajuda a confirmar se nenhuma referência inesperada a objetos permanece e se a exclusão/coleta de lixo está realmente funcionando.

Para alternar entre as visualizações, use o seletor na parte inferior da visualização:

Seletor de alternância de visualizações

Observação: nem todas as propriedades são armazenadas na pilha do JavaScript. As propriedades implementadas que usam elementos que executam código nativo não são capturadas. Além disso, os valores que não são strings, como números, não são capturados.

Visualização Summary

Inicialmente, um instantâneo abre a visualização Summary, exibindo totais do objeto que podem ser expandidos para mostrar instâncias:

Visualização Summary

As entradas de nível superior são linhas de "total". Elas exibem:

  • Constructor representa todos os objetos criados usando este construtor.
  • O número de instâncias no objeto é exibido na # coluna.
  • A coluna Shallow size exibe a soma de tamanhos superficiais de todos os objetos criados por determinada função do construtor. O shallow size (tamanho superficial) é o tamanho da memória detida por um objeto (geralmente, matrizes e strings têm tamanhos superficiais maiores). Veja também Tamanhos de objeto.
  • A coluna Retained size exibe o tamanho retido máximo dentre o mesmo conjunto de objetos. A quantidade de memória que pode ser liberada depois que um objeto é excluído (e isso torna seus dependentes inacessíveis) é chamada de tamanho retido. Veja também Tamanhos de objeto.
  • Distance exibe a distância para a raiz usando o caminho de nós simples mais curto.

A expansão de uma linha de total na visualização superior exibe todas as suas instâncias. Para cada instância, os tamanhos superficial e retido são exibidos nas colunas correspondentes. O número depois do caractere @ é o ID único do objeto, o que permite comparar instantâneos de pilha por objeto.

Lembre-se de que os objetos amarelos têm referências ao JavaScript e os objetos vermelhos são nós desconectados referenciados por um com fundo amarelo.

A que correspondem as diversas entradas de construtor (grupo) no criador de perfil de pilha?

Grupos de construtor

  • (global property) – intermedeia objetos entre um objeto global (como "janela") e um objeto referenciado por ele. Se um objeto for criado usando um construtor Person e for detido por um objeto global, o caminho de retenção será [global] > (global property) > Person. Isto contrasta com a norma, onde os objetos referenciam-se mútua e diretamente. Temos objetos intermediários para fins de desempenho. Globais são modificados regularmente e as otimizações de acesso à propriedade fazem um bom trabalho com objetos não globais que não são aplicáveis para globais.

  • (roots) – As entradas da raiz na vista da árvore de retenção são as entidades que têm referências ao objeto selecionado. Elas também podem ter referências criadas pelo mecanismo por finalidades próprias. O mecanismo tem caches que referenciam objetos, mas todas essas referências são fracas e não impedem que um objeto seja coletado, já que não há referências realmente sólidas.

  • (closure) – uma contagem de referências a um grupo de objetos por meio de fechamentos de função

  • (array, string, number, regexp) – uma lista de tipos de objeto com propriedades que referenciam uma Matriz, uma String, um Número ou expressão regular.

  • (compiled code) – simplesmente tudo relacionado ao código compilado. Script é parecido com uma função, mas corresponde ao corpo de um <script>. SharedFunctionInfos (SFI) são objetos que ficam entre funções e o código compilado. As funções normalmente têm um contexto, enquanto que os SFIs, não.

  • HTMLDivElement, HTMLAnchorElement, DocumentFragment etc. – referências a elementos ou objetos de documento de um determinado tipo referenciados pelo código.

Exemplo: experimente esta página de demonstração para compreender como a visualização Summary pode ser usada.

Visualização Comparison

Encontre objetos com vazamento comparando vários instantâneos entre si. Para verificar se uma determinada operação do aplicativo não cria vazamentos (por exemplo, normalmente um par de operações direta e inversa, como abrir um documento e depois fechá-lo, não deve deixar nenhum lixo), você pode seguir este cenário:

  1. Capture um instantâneo da pilha antes de realizar uma operação.
  2. Realize uma operação (interaja com uma página de uma forma que acredite que cause um vazamento).
  3. Realize uma operação inversa (faça a interação oposta e repita-a algumas vezes).
  4. Capture um segundo instantâneo da pilha e altere sua visualização para Comparison, comparando-o com o instantâneo 1.

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

Visualização Comparison

Exemplo: experimente esta página de demonstração para ter uma ideia de como usar a comparação de instantâneos para detectar vazamentos.

Visualização Containment

A visualização Containment é essencialmente uma "visualização geral" da estrutura dos objetos de um aplicativo. Ela permite observar dentro de fechamentos de funções para ver os objetos internos da VM que compõem os objetos do JavaScript e compreender quanta memória o aplicativo usa em um nível bem baixo.

A visualização oferece diversos pontos de entrada:

  • Objetos DOMWindow são considerados objetos "globais" para o código JavaScript.
  • Raízes GC são as raízes GC efetivamente usadas pelo lixo da MV. As raízes GC podem ser compostas de mapas de objeto embutidos, tabelas de símbolo, pilhas de encadeamento da MV, caches de compilação, escopos de identificador e identificadores globais.
  • Objetos nativos são objetos do navegador inseridos na máquina virtual do JavaScript para permitir automação como, por exemplo, nós do DOM e regras CSS.

Visualização Containment

Exemplo: experimente esta página de demonstração para descobrir como explorar fechamentos e gerenciadores de evento usando a visualização.

Uma dica sobre fechamentos

É muito útil nomear as funções para permitir distinguir facilmente os fechamentos no instantâneo. Por exemplo, este exemplo 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 usa:

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

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

  return lC;
}

Nomear funções para distinguir fechamentos

Exemplos: experimente este exemplo de por que o eval é ruim para analisar o impacto de fechamentos na memória. Você também pode ter interesse em continuar com este exemplo que mostra a gravação de alocações de pilha.

Visualização Dominators

A visualização Dominators exibe a árvore de dominadores para o gráfico de pilha. Ela é parecida com a visualização Containment, mas não tem o nome das propriedades. Isso ocorre porque um dominador de um objeto pode não ter referências diretas a ele; a árvore de dominadores não é uma árvore abrangente do gráfico. Mas isso ajuda, já que é útil para identificar rapidamente pontos de acúmulo de memória.

Observação: no Chrome Canary, a visualização Dominators pode ser ativada acessando Settings > Show advanced heap snapshot properties e reiniciando o DevTools.

Visualização Dominators

Exemplos: experimente esta demonstração para praticar a procura de pontos de acúmulo. Continue com este exemplo de encontrar caminhos de retenção e dominadores.

Buscar codificação em cores

As propriedades e os valores de propriedade de objetos têm diferentes tipos e, por isso, são coloridos. Cada propriedade tem um dos quatro tipos:

  • a: property — propriedade regular com um nome, acessado pelo operador . (ponto), ou por notação em [ ] (colchetes), por exemplo, ["foo bar"];
  • 0: element — propriedade regular com um índice numérico, acessada por notação em [ ] (colchetes);
  • a: context var — variável no contexto de uma função, acessível por seu nome de dentro de um fechamento de função;
  • a: system prop — propriedade adicionada pela VM do JavaScript, não acessível pelo código JavaScript.

Objetos designados como Systemnão têm um tipo de JavaScript correspondente. Elas fazem parte da implementação do sistema de objetos da MV JavaScript. O V8 aloca a maior parte dos seus objetos internos na mesma pilha que os objetos JS do usuário. Esses são apenas detalhes internos do v8.

Encontrar um objeto específico

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

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, regras CSS) e objetos JavaScript. Isso ajuda a descobrir eventuais 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. Considere o exemplo a seguir - quando #tree é GC?

  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 can be #tree GC

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

Subárvores do DOM

Exemplos: experimente este exemplo de nós do DOM com vazamento para entender onde os nós do DOM podem vazar e como detectar esses vazamentos. Você pode continuar examinando este exemplo de vazamentos no DOM maiores do que o esperado.

Para ler mais sobre os vazamentos no DOM e fundamentos da análise de memória, confira Como encontrar e depurar vazamentos de memória com o Chrome DevTools, de Gonzalo Ruiz de Villa.

Exemplo: experimente esta demonstração para testar árvores do DOM desconectadas.