Criei um caso de teste muito simples que cria uma visualização Backbone, anexa um manipulador a um evento e instancia uma classe definida pelo usuário. Acredito que, ao clicar no botão "Remover" neste exemplo, tudo será limpo e não haverá vazamento de memória.
Um jsfiddle para o código está aqui: http://jsfiddle.net/4QhR2/
// scope everything to a function
function main() {
function MyWrapper() {
this.element = null;
}
MyWrapper.prototype.set = function(elem) {
this.element = elem;
}
MyWrapper.prototype.get = function() {
return this.element;
}
var MyView = Backbone.View.extend({
tagName : "div",
id : "view",
events : {
"click #button" : "onButton",
},
initialize : function(options) {
// done for demo purposes only, should be using templates
this.html_text = "<input type='text' id='textbox' /><button id='button'>Remove</button>";
this.listenTo(this,"all",function(){console.log("Event: "+arguments[0]);});
},
render : function() {
this.$el.html(this.html_text);
this.wrapper = new MyWrapper();
this.wrapper.set(this.$("#textbox"));
this.wrapper.get().val("placeholder");
return this;
},
onButton : function() {
// assume this gets .remove() called on subviews (if they existed)
this.trigger("cleanup");
this.remove();
}
});
var view = new MyView();
$("#content").append(view.render().el);
}
main();
No entanto, não estou claro como usar o criador de perfil do Google Chrome para verificar se esse é realmente o caso. Há um zilhão de coisas que aparecem no instantâneo do heap profiler, e não tenho idéia de como decodificar o que é bom / ruim. Os tutoriais que eu vi até agora ou apenas me dizem para "usar o snapshot profiler" ou me dão um manifesto detalhado sobre como o profiler inteiro funciona. É possível apenas usar o criador de perfil como uma ferramenta, ou eu realmente tenho que entender como tudo foi projetado?
EDIT: Tutoriais como estes:
Correção de vazamento de memória do Gmail
São representativos de alguns dos materiais mais fortes por aí, pelo que vi. No entanto, além de introduzir o conceito da 3 Snapshot Technique , acho que eles oferecem muito pouco em termos de conhecimento prático (para um iniciante como eu). O tutorial 'Usando o DevTools' não funciona com um exemplo real, portanto, sua descrição conceitual vaga e geral das coisas não é muito útil. Quanto ao exemplo 'Gmail':
Então você encontrou um vazamento. O que agora?
Examine o caminho de retenção de objetos vazados na metade inferior do painel Perfis
Se o site de alocação não puder ser facilmente deduzido (ou seja, ouvintes de eventos):
Instrumentar o construtor do objeto de retenção por meio do console JS para salvar o rastreamento de pilha para alocações
Usando Closure? Ative o sinalizador existente apropriado (por exemplo, goog.events.Listener.ENABLE_MONITORING) para definir a propriedade creationStack durante a construção
Eu me sinto mais confusa depois de ler isso, não menos. E, novamente, está apenas me dizendo para fazer as coisas, não como fazê-las. Do meu ponto de vista, todas as informações existentes são muito vagas ou apenas fazem sentido para alguém que já entendeu o processo.
Algumas dessas questões mais específicas foram levantadas na resposta de Jonathan Naguin abaixo.
fonte
main
10.000 vezes em vez de uma vez e verificar se você acaba com muito mais memória em uso no final.Respostas:
Um bom fluxo de trabalho para encontrar vazamentos de memória é a técnica de três instantâneos , usada pela primeira vez por Loreena Lee e pela equipe do Gmail para resolver alguns de seus problemas de memória. As etapas são, em geral:
Para o seu exemplo, adaptei o código para mostrar esse processo (você pode encontrá-lo aqui ) atrasando a criação da exibição de backbone até o evento de clique do botão Iniciar. Agora:
Agora você está pronto para encontrar vazamentos de memória!
Você notará nós de algumas cores diferentes. Nós vermelhos não têm referências diretas do Javascript para eles, mas estão ativos porque fazem parte de uma árvore DOM desanexada. Pode haver um nó na árvore referenciado pelo Javascript (talvez como um fechamento ou variável), mas coincidentemente está impedindo que a árvore DOM inteira seja coletada como lixo.
Nós amarelos, no entanto, têm referências diretas do Javascript. Procure nós amarelos na mesma árvore DOM desanexada para localizar referências do seu Javascript. Deve haver uma cadeia de propriedades que leva da janela DOM para o elemento.
No seu particular, você pode ver um elemento HTML Div marcado como vermelho. Se você expandir o elemento, verá que é referenciado por uma função "cache".
Selecione a linha e, no console, digite $ 0, você verá a função e o local reais:
É aqui que seu elemento está sendo referenciado. Infelizmente, não há muito o que você possa fazer, é um mecanismo interno do jQuery. Mas, apenas para fins de teste, acesse a função e altere o método para:
Agora, se você repetir o processo, não verá nenhum nó vermelho :)
Documentação:
fonte
$0
função no console, que era nova para mim - é claro, não tenho idéia do que está fazendo ou como você sabia usá-la ($1
parece inútil, enquanto$2
parece fazer a mesma coisa). Em segundo lugar, como você sabia destacar a linha#button in function cache()
e não nenhuma das outras dezenas de linhas? Finalmente, existem nós vermelhosNodeList
eHTMLInputElement
também, mas não consigo descobrir.cache
linha continha informações enquanto as outras não? Existem inúmeros ramos que têm uma distância menor que a delecache
. E não tenho certeza de como você sabia que issoHTMLInputElement
é filho deHTMLDivElement
. Eu o vejo referenciado dentro dele ("nativo em HTMLDivElement"), mas também se refere a ele e doisHTMLButtonElement
s, o que não faz sentido para mim. Certamente, agradeço sua identificação da resposta para este exemplo, mas realmente não tenho idéia de como generalizar isso para outras questões.Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.
significa isso ?Aqui está uma dica sobre o perfil de memória de um jsfiddle: Use o seguinte URL para isolar o resultado do jsfiddle, ele remove toda a estrutura do jsfiddle e carrega apenas o resultado.
http://jsfiddle.net/4QhR2/show/
Nunca consegui descobrir como usar a Linha do tempo e o Profiler para rastrear vazamentos de memória, até ler a documentação a seguir. Depois de ler a seção intitulada 'Rastreador de alocação de objetos', pude usar a ferramenta 'Alocações de heap de registros' e rastrear alguns nós do DOM desanexado.
Corrigi o problema passando da ligação de evento jQuery para a delegação de eventos Backbone. Entendo que as versões mais recentes do Backbone desvincularão automaticamente os eventos para você, se você ligar
View.remove()
. Execute você mesmo algumas demos, elas são configuradas com vazamentos de memória para você identificar. Sinta-se à vontade para fazer perguntas aqui, se você ainda não o receber depois de estudar esta documentação.https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling
fonte
Basicamente, você precisa observar o número de objetos dentro de seu instantâneo de heap. Se o número de objetos aumentar entre dois instantâneos e você tiver descartado objetos, haverá um vazamento de memória. Meu conselho é procurar manipuladores de eventos no seu código que não sejam desanexados.
fonte
Window/http://jsfiddle.net/4QhR2/show
poderia ser útil, mas são apenas infinitas funções. Não tenho ideia do que está acontecendo lá.Existe um vídeo de introdução do Google, que será muito útil para encontrar vazamentos de memória JavaScript.
https://www.youtube.com/watch?v=L3ugr9BJqIs
fonte
Você também pode consultar a guia Linha do tempo nas ferramentas do desenvolvedor. Registre o uso do seu aplicativo e fique de olho na contagem de ouvintes de nós e eventos do DOM.
Se o gráfico de memória realmente indicar um vazamento de memória, você poderá usar o criador de perfil para descobrir o que está vazando.
fonte
Você também pode querer ler:
http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/
Ele explica o uso das ferramentas de desenvolvedor do Chrome e fornece alguns conselhos passo a passo sobre como confirmar e localizar um vazamento de memória usando a comparação de instantâneos de heap e as diferentes visualizações de instantâneos hep disponíveis.
fonte
Segui o conselho de tirar um instantâneo de pilha, eles são excelentes para detectar vazamentos de memória, o chrome faz um excelente trabalho de captura de instantâneos.
No meu projeto de pesquisa para minha graduação, eu estava criando um aplicativo da web interativo que precisava gerar muitos dados construídos em 'camadas', muitas dessas camadas seriam 'excluídas' na interface do usuário, mas, por algum motivo, a memória não era sendo desalocado, usando a ferramenta de captura instantânea, pude determinar que o JQuery mantinha uma referência no objeto (a fonte era quando eu estava tentando acionar um
.load()
evento que mantinha a referência apesar de sair do escopo). Ter essas informações em mãos salvou sozinho meu projeto, é uma ferramenta muito útil quando você está usando as bibliotecas de outras pessoas e você tem esse problema de referências persistentes que impedem o GC de fazer seu trabalho.EDIT: Também é útil planejar com antecedência quais ações você executará para minimizar o tempo gasto em snapshots, colocar a hipótese do que poderia estar causando o problema e testar cada cenário, fazendo snapshots antes e depois.
fonte
Algumas observações importantes sobre a identificação de vazamentos de memória usando as ferramentas do desenvolvedor do Chrome:
1) O próprio Chrome possui vazamentos de memória para certos elementos, como campos de senha e número. https://bugs.chromium.org/p/chromium/issues/detail?id=967438 . Evite usá-los durante a depuração, pois eles poluem seu instantâneo de heap ao procurar elementos desanexados.
2) Evite registrar qualquer coisa no console do navegador. O Chrome não coleta objetos gravados no console, afetando o resultado. Você pode suprimir a saída colocando o seguinte código no início do seu script / página:
3) Use instantâneos de heap e procure por "desanexar" para identificar elementos DOM desanexados. Ao passar o mouse sobre os objetos, você obtém acesso a todas as propriedades, incluindo id e outerHTML, que podem ajudar a identificar cada elemento. Se os elementos desanexados ainda forem muito genéricos para reconhecer, atribua a eles IDs exclusivos usando o console do navegador antes de executar seu teste, por exemplo:
Agora, quando você identifica um elemento desanexado com, digamos id = "AutoId_49", recarregue sua página, execute o trecho acima novamente e encontre o elemento com id = "AutoId_49" usando o inspetor DOM ou document.querySelector (..) . Naturalmente, isso só funciona se o conteúdo da sua página for previsível.
Como executo meus testes para identificar vazamentos de memória
1) Carregar página (com a saída do console suprimida!)
2) Faça coisas na página que possam resultar em vazamento de memória
3) Use as Ferramentas do desenvolvedor para tirar um instantâneo da pilha e procurar "desanexar"
4) Passe o mouse sobre os elementos para identificá-los a partir de suas propriedades id ou outerHTML
fonte