2011-08-25 14 views
13

Estoy trabajando en una aplicación para grandes empresas con MUCHO JavaScript. Suficiente como para no poder revisar todas las pequeñas referencias circulares que se han creado en los últimos 5 años de desarrollo. Durante la investigación de soluciones me encontré con este pequeño truco jQuery/parche:fugas de jQuery resueltas, pero ¿por qué?

http://kossovsky.net/index.php/2009/07/ie-memory-leak-jquery-garbage-collector/

y decidí probarlo. Sorprendentemente, ¡funciona! sIEVE no muestra filtraciones en los lugares que yo había identificado previamente y la tarea iexplore mantiene una huella de memoria más manejable.

Mi pregunta es, ¿por qué funciona esto? jQuery.remove llama a .removeChild, que debería deshacerse del elemento, pero aparentemente no lo hace. En cambio, el parche agrega el elemento de destino a un nuevo divctor de recolector de elementos no utilizados, que luego borra. ¿Por qué el método de eliminación de parches libera completamente la memoria, pero la función de eliminación de jQuery no? Espero entender por qué esto funciona para poder posiblemente mejorar la solución antes de registrarla en la aplicación más grande.

Respuesta

12

Este es el método .remove en la versión actual de jQuery (1.6.2). Observe que se llama .cleanData:

// keepData is for internal use only--do not document 
    remove: function(selector, keepData) { 
     for (var i = 0, elem; (elem = this[i]) != null; i++) { 
      if (!selector || jQuery.filter(selector, [ elem ]).length) { 
       if (!keepData && elem.nodeType === 1) { 
        jQuery.cleanData(elem.getElementsByTagName("*")); 
        jQuery.cleanData([ elem ]); 
       } 

       if (elem.parentNode) { 
        elem.parentNode.removeChild(elem); 
       } 
      } 
     } 

     return this; 
    }, 

Y el método .cleanData que llama, que menciona un número de ticket y supuestamente evita esa horrible fugas (de acuerdo con uno de los comentarios):

cleanData: function(elems) { 
     var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special, 
      deleteExpando = jQuery.support.deleteExpando; 

     for (var i = 0, elem; (elem = elems[i]) != null; i++) { 
      if (elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) { 
       continue; 
      } 

      id = elem[ jQuery.expando ]; 

      if (id) { 
       data = cache[ id ] && cache[ id ][ internalKey ]; 

       if (data && data.events) { 
        for (var type in data.events) { 
         if (special[ type ]) { 
          jQuery.event.remove(elem, type); 

         // This is a shortcut to avoid jQuery.event.remove's overhead 
         } else { 
          jQuery.removeEvent(elem, type, data.handle); 
         } 
        } 

        // Null the DOM reference to avoid IE6/7/8 leak (#7054) 
        if (data.handle) { 
         data.handle.elem = null; 
        } 
       } 

       if (deleteExpando) { 
        delete elem[ jQuery.expando ]; 

       } else if (elem.removeAttribute) { 
        elem.removeAttribute(jQuery.expando); 
       } 

       delete cache[ id ]; 
      } 
     } 
    } 

Y aquí es el boleto mencionado en el comentario. Al parecer, se fijó hace ocho meses:

http://bugs.jquery.com/ticket/7054#comment:10

Según Dave Methvin la solución parece ser la de Asegúrese de que el árbitro elemento DOM en un controlador de eventos se elimina por CleanData para evitar un IE6/7/8 pérdida de memoria.

En otras palabras, establecer referencias a elementos DOM dentro de controladores de eventos a null de lo contrario algunos navegadores impresionante, sin mencionar nombres tos IE tos perderá memoria.

discardElement (de su enlace) inserta el elemento en un contenedor, y luego vacía el contenedor, por lo tanto anulando cualquier referencia a ese elemento.

Teniendo esto en cuenta, le sugiero actualizar jQuery. El artículo que señala es de 2009, y dos años equivale aproximadamente a cuatrocientos trillones de horas hombre de tiempo de desarrollo de jQuery.

Por último, aquí es una lectura interesante (y ridículamente largo) en los patrones de fugas en Internet Explorer:

+0

Por desgracia, no puede ir razonablemente a través de toda la base de código en busca de controladores de eventos anular Por eso esta intrincada solución me intriga. Hasta el momento se ha demostrado que es algo así como una bala de plata que no requiere el ajuste manual de los manejadores para anular. – JPRO

+0

@JPRO - no controladores de eventos, elementos DOM dentro de controladores de eventos, y no tienes que hacer eso si estás dispuesto a actualizar jQuery a una versión más reciente. – karim79

+0

Ciertamente, el código incorrecto ha sido uno de los principales contribuyentes al problema, y ​​podría revisarlo todo y corregirlo, pero tomaría mucho más tiempo de lo que he asignado para esta tarea. Actualmente estamos usando jQuery 1.6.2. Dicho esto, creo que has respondido la pregunta de por qué funciona esto (yo mismo llegué a la misma conclusión, pero quería más información). Gracias por la info! – JPRO

1

Voy a teorizar que es similar a la recolección de basura .net, ya que se basa en objetos fijados en el montón.

IE trata al elemento primario de un objeto eliminado como un alfiler y no despeja el objeto eliminado correctamente.

El hecho de mover el elemento eliminado a este contenedor de gc generado básicamente consiste en quitar el pin porque IE sabe que nada depende de ese contenedor.

Esa es mi corazonada de todos modos.

Cuestiones relacionadas