punto online interrupcion depurar debugger consola chrome javascript google-chrome backbone.js memory-leaks

javascript - online - js heap



Encontrar fugas de memoria JavaScript con Chrome (7)

Aquí hay un consejo sobre el perfil de memoria de un jsfiddle: Use el siguiente URL para aislar su resultado jsfiddle, elimina todo el framework jsfiddle y solo carga su resultado.

http://jsfiddle.net/4QhR2/show/

Nunca pude descifrar cómo usar Timeline y Profiler para rastrear las pérdidas de memoria, hasta que leí la siguiente documentación. Después de leer la sección titulada ''Localizador de asignación de objetos'', pude utilizar la herramienta ''Registrar asignaciones de heap'' y rastrear algunos nodos DOM separados.

Solucioné el problema cambiando el enlace del evento jQuery al uso de la delegación del evento Backbone. Tengo entendido que las versiones más nuevas de Backbone desvincularán automáticamente los eventos si llama a View.remove() . Ejecute algunas de las demostraciones usted mismo, están configuradas con pérdidas de memoria para que pueda identificar. No dude en hacer preguntas aquí si todavía no lo obtiene después de estudiar esta documentación.

https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling

Creé un caso de prueba muy simple que crea una vista Backbone, adjunta un controlador a un evento y crea una instancia de 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 generador de perfiles de Google Chrome para verificar que este sea, de hecho, el caso. Hay un montón de cosas que aparecen en la instantánea del perfilador de montón, y no tengo idea de cómo decodificar lo que está bien / mal. Los tutoriales que he visto hasta ahora me dicen simplemente "usar el generador de instantáneas" o me dan un manifiesto enormemente detallado sobre cómo funciona todo el generador de perfiles. ¿Es posible usar el perfilador como herramienta, o realmente tengo que entender cómo se diseñó todo?

EDITAR: Tutoriales como estos:

Fijación de fuga de memoria Gmail

Usando DevTools

Son representativos de algunos de los materiales más potentes, por lo que he visto. Sin embargo, más allá de introducir 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 ''Usar 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 filtración. ¿Ahora que?

  • Examine el camino de retención de los objetos filtrados en la mitad inferior del panel Perfiles

  • Si el sitio de asignación no puede inferirse fácilmente (es decir, los detectores de eventos):

  • Instrumente el constructor del objeto de retención a través de la consola JS para guardar el seguimiento de pila para las asignaciones

  • ¿Usando el 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 está diciendo que haga las 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.


Básicamente, debe observar el número de objetos dentro de su instantánea de montón. Si la cantidad de objetos aumenta entre dos instantáneas y se deshace de ellos, entonces tiene una pérdida de memoria. Mi consejo es buscar controladores de eventos en tu código que no se separen.


En segundo lugar los consejos para tomar una instantánea del montón, son excelentes para detectar fugas de memoria, Chrome hace un excelente trabajo de captura de imágenes.

En mi proyecto de investigación para mi título estaba construyendo 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 ha estado manteniendo una referencia en el objeto (la fuente fue cuando estaba tratando de desencadenar un evento .load() que mantuvo la referencia a pesar de estar fuera del alcance). Al tener esta información a mano, solo guardé mi proyecto, es una herramienta muy útil cuando está usando bibliotecas de otras personas y tiene este problema de referencias persistentes que impide que el GC haga su trabajo.

EDITAR: También es útil planificar con anticipación las acciones que va a realizar para minimizar el tiempo dedicado a la captura de instantáneas, hipotetizar qué podría estar causando el problema y probar cada escenario, haciendo instantáneas antes y después.




También puede mirar la pestaña Línea de tiempo en las herramientas del desarrollador. Registre el uso de su aplicación y esté atento al conteo de nodos DOM y eventos.

Si el gráfico de memoria indica una fuga de memoria, puede usar el generador de perfiles para averiguar qué se está filtrando.


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:

  • Toma una instantánea de montón.
  • Hacer cosas.
  • Toma otra instantánea de montón.
  • Repite lo mismo.
  • Toma otra instantánea de montón.
  • Filtre los objetos asignados entre las instantáneas 1 y 2 en la vista "Resumen" de la instantánea 3.

Para su ejemplo, he adaptado el código para mostrar este proceso (puede encontrarlo here ) retrasando la creación de la Vista Backbone hasta el evento click del botón Inicio. Ahora:

  • Ejecute el HTML (guardado localmente al usar esta address ) y tome una instantánea.
  • Haga clic en Iniciar para crear la vista.
  • Toma otra instantánea.
  • Haga clic en eliminar.
  • Toma otra instantánea.
  • Filtre los objetos asignados entre las instantáneas 1 y 2 en la vista "Resumen" de la instantánea 3.

¡Ahora estás listo para encontrar fugas de memoria!

Notará nodos de algunos colores diferentes. Los nodos rojos no tienen referencias directas de Javascript para ellos, pero están vivos porque son parte de un árbol DOM independiente. Puede haber un nodo en el árbol al que se hace referencia desde Javascript (tal vez como un cierre o una variable) pero que casualmente impida que todo el árbol DOM sea recogido como basura.

Los nodos amarillos, sin embargo, tienen referencias directas de Javascript. Busque nodos amarillos en el mismo árbol de DOM separado para localizar referencias de su Javascript. Debe haber una cadena de propiedades que va de la ventana DOM al elemento.

En su particular, puede ver un elemento HTML Div marcado como rojo. Si expandes el elemento, verás que está referenciado por una función de "caché".

Seleccione la fila y en su tipo de consola $ 0, verá la función y ubicación reales:

>$0 function cache( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) if ( keys.push( key += " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } return (cache[ key ] = value); } jquery-2.0.2.js:1166

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:

function cache( key, value ) { return value; }

Ahora, si repites el proceso, no verás ningún nodo rojo :)

Documentación: