He creado un caso de prueba muy simple que crea una vista Backbone, adjunta un controlador a un evento e instancia una clase definida por el usuario. Creo que al hacer clic en el botón "Eliminar" en esta muestra, todo se limpiará y no debería haber pérdidas de memoria.
Un jsfiddle para el código está aquí: 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();
Sin embargo, no tengo claro cómo usar el perfilador de Google Chrome para verificar que este sea, de hecho, el caso. Hay miles de millones de cosas que aparecen en la instantánea del generador de perfiles del montón, y no tengo idea de cómo decodificar lo que es bueno / malo. Los tutoriales que he visto hasta ahora solo me dicen que "use el generador de perfiles de instantáneas" o me dan un manifiesto enormemente detallado sobre cómo funciona todo el generador de perfiles. ¿Es posible usar el generador de perfiles como una herramienta, o realmente tengo que entender cómo se diseñó todo?
EDITAR: Tutoriales como estos:
Solución de fugas de memoria de Gmail
Son representativos de algunos de los materiales más fuertes que existen, por lo que he visto. Sin embargo, más allá de presentar el concepto de la técnica de 3 instantáneas , encuentro que ofrecen muy poco en términos de conocimiento práctico (para un principiante como yo). El tutorial 'Uso de DevTools' no funciona a través de un ejemplo real, por lo que su descripción conceptual vaga y general de las cosas no es demasiado útil. En cuanto al ejemplo de 'Gmail':
Entonces encontraste una fuga. ¿Ahora que?
Examine la ruta de retención de los objetos filtrados en la mitad inferior del panel Perfiles.
Si el sitio de asignación no se puede inferir fácilmente (es decir, oyentes de eventos):
Instrumente al constructor del objeto de retención a través de la consola JS para guardar el seguimiento de la pila para asignaciones
¿Usando cierre? Habilite el indicador existente apropiado (es decir, goog.events.Listener.ENABLE_MONITORING) para establecer la propiedad creationStack durante la construcción
Me encuentro más confundido después de leer eso, no menos. Y, de nuevo, solo me dice que haga cosas, no cómo hacerlas. Desde mi punto de vista, toda la información que existe es demasiado vaga o solo tendría sentido para alguien que ya entendió el proceso.
Algunos de estos problemas más específicos se han planteado en la respuesta de @Jonathan Naguin a continuación.
fuente
main
10,000 veces en lugar de una vez, y ver si al final tiene mucha más memoria en uso.Respuestas:
Un buen flujo de trabajo para encontrar fugas de memoria es la técnica de tres instantáneas , utilizada por primera vez por Loreena Lee y el equipo de Gmail para resolver algunos de sus problemas de memoria. Los pasos son, en general:
Para su ejemplo, he adaptado el código para mostrar este proceso (puede encontrarlo aquí ) retrasando la creación de la Vista Backbone hasta el evento de clic del botón Inicio. Ahora:
¡Ahora estás listo para encontrar pérdidas de memoria!
Notará nodos de algunos colores diferentes. Los nodos rojos no tienen referencias directas de Javascript a ellos, pero están vivos porque son parte de un árbol DOM separado. Puede haber un nodo en el árbol al que se hace referencia desde Javascript (tal vez como un cierre o variable), pero casualmente impide que se recolecte basura todo el árbol DOM.
Sin embargo, los nodos amarillos tienen referencias directas de Javascript. Busque nodos amarillos en el mismo árbol DOM separado para ubicar referencias de su Javascript. Debe haber una cadena de propiedades que conduzca desde la ventana DOM al elemento.
En su particular, puede ver un elemento Div HTML marcado como rojo. Si expande el elemento verá que hace referencia a una función "caché".
Seleccione la fila y en su consola escriba $ 0, verá la función y ubicación reales:
Aquí es donde se hace referencia a su elemento. Desafortunadamente no hay mucho que puedas hacer, es un mecanismo interno de jQuery. Pero, solo para fines de prueba, vaya a la función y cambie el método a:
Ahora, si repite el proceso, no verá ningún nodo rojo :)
Documentación:
fuente
$0
función en la consola, que era nueva para mí; por supuesto, no tengo idea de qué está haciendo o cómo sabías usarla ($1
parece inútil mientras$2
parece hacer lo mismo). En segundo lugar, ¿cómo sabías resaltar la fila#button in function cache()
y no ninguna de las otras docenas de filas? Por último, hay nodos rojos enNodeList
yHTMLInputElement
también, pero no pueden descifrarlos.cache
fila contenía información mientras que los demás no? Hay numerosas ramas que tienen una distancia menor que lacache
. Y no estoy seguro de cómo sabías queHTMLInputElement
es hijo deHTMLDivElement
. Lo veo referenciado dentro de él ("nativo en HTMLDivElement"), pero también hace referencia a sí mismo y dosHTMLButtonElement
s, lo que no tiene sentido para mí. Ciertamente aprecio que haya identificado la respuesta para este ejemplo, pero realmente no tendría idea de cómo generalizar esto a otros temas.Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.
significaAquí hay un consejo sobre el perfil de memoria de un jsfiddle: use la siguiente URL para aislar su resultado jsfiddle, elimina todo el marco jsfiddle y carga solo su resultado.
http://jsfiddle.net/4QhR2/show/
Nunca pude descubrir cómo usar la línea de tiempo y el generador de perfiles para rastrear pérdidas de memoria, hasta que leí la siguiente documentación. Después de leer la sección titulada 'Rastreador de asignación de objetos' pude usar la herramienta 'Grabar asignaciones de montón' y rastrear algunos nodos DOM separados.
Solucioné el problema al cambiar del enlace de eventos jQuery a usar la delegación de eventos Backbone. Tengo entendido que las versiones más recientes de Backbone desvincularán automáticamente los eventos si llama
View.remove()
. Ejecute algunas de las demostraciones usted mismo, están configuradas con pérdidas de memoria para que pueda identificarlas. Siéntase libre de hacer preguntas aquí si aún no lo recibe después de estudiar esta documentación.https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling
fuente
Básicamente, debe mirar la cantidad de objetos dentro de su instantánea de montón. Si el número de objetos aumenta entre dos instantáneas y ha eliminado los objetos, entonces tiene una pérdida de memoria. Mi consejo es buscar controladores de eventos en su código que no se separen.
fuente
Window/http://jsfiddle.net/4QhR2/show
podría ser útil, pero son funciones interminables. No tengo idea de lo que está pasando allí.Hay un video de introducción de Google, que será muy útil para encontrar fugas de memoria de JavaScript.
https://www.youtube.com/watch?v=L3ugr9BJqIs
fuente
También puede mirar la pestaña Línea de tiempo en las herramientas para desarrolladores. Registre el uso de su aplicación y vigile el recuento de oyentes del nodo DOM y el evento.
Si el gráfico de memoria indicara una pérdida de memoria, entonces puede usar el generador de perfiles para averiguar qué está perdiendo.
fuente
También es posible que desee leer:
http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/
Explica el uso de las herramientas para desarrolladores de Chrome y ofrece algunos consejos paso a paso sobre cómo confirmar y localizar una pérdida de memoria mediante la comparación de instantáneas de montón y las diferentes vistas de instantáneas hep disponibles.
fuente
Respaldo el consejo de tomar una instantánea del montón, son excelentes para detectar fugas de memoria, Chrome hace un excelente trabajo de instantáneas.
En mi proyecto de investigación para mi título, estaba creando una aplicación web interactiva que tenía que generar una gran cantidad de datos acumulados en 'capas', muchas de estas capas serían 'eliminadas' en la interfaz de usuario, pero por alguna razón la memoria no era al ser desasignado, usando la herramienta de instantáneas pude determinar que JQuery había estado manteniendo una referencia en el objeto (la fuente fue cuando estaba tratando de desencadenar un
.load()
evento que mantuvo la referencia a pesar de estar fuera de alcance). Tener esta información a la mano guardó mi proyecto sin ayuda, es una herramienta muy útil cuando está utilizando las bibliotecas de otras personas y tiene este problema de referencias persistentes que impiden que el GC haga su trabajo.EDITAR: También es útil planificar con anticipación qué acciones va a realizar para minimizar el tiempo dedicado a las instantáneas, plantear la hipótesis de lo que podría estar causando el problema y probar cada escenario, haciendo instantáneas antes y después.
fuente
Un par de notas importantes con respecto a la identificación de pérdidas de memoria con las herramientas de desarrollador de Chrome:
1) Chrome tiene pérdidas de memoria para ciertos elementos, como los campos de contraseña y número. https://bugs.chromium.org/p/chromium/issues/detail?id=967438 . Evite usarlos durante la depuración, ya que contaminan su instantánea del montón al buscar elementos separados.
2) Evite registrar cualquier cosa en la consola del navegador. Chrome no recolectará basura los objetos escritos en la consola, lo que afectará su resultado. Puede suprimir la salida colocando el siguiente código al comienzo de su script / página:
3) Use instantáneas de montón y busque "separar" para identificar elementos DOM separados. Al pasar el cursor sobre los objetos, obtienes acceso a todas las propiedades, incluidos id y externalHTML, que pueden ayudar a identificar cada elemento. Si los elementos separados siguen siendo demasiado genéricos para reconocerlos, asígneles ID únicos utilizando la consola del navegador antes de ejecutar su prueba, por ejemplo:
Ahora, cuando identifique un elemento separado con, digamos id = "AutoId_49", vuelva a cargar su página, ejecute el fragmento de arriba y encuentre el elemento con id = "AutoId_49" utilizando el inspector DOM o document.querySelector (..) . Naturalmente, esto solo funciona si el contenido de su página es predecible.
Cómo ejecuto mis pruebas para identificar pérdidas de memoria
1) Cargar página (con salida de consola suprimida)
2) Haga cosas en la página que podrían provocar pérdidas de memoria
3) Use las herramientas de desarrollador para tomar una instantánea del montón y busque "separar"
4) Desplace los elementos para identificarlos desde sus propiedades id o externalHTML
fuente