2008-11-09 17 views
7

Estoy trabajando en una aplicación web que está diseñada para mostrar un conjunto de datos que se actualiza periódicamente con AJAX. El escenario de uso general sería que un usuario lo dejara abierto todo el día y lo echara un vistazo de vez en cuando.Prevención de fugas de memoria AJAX

Me encuentro con un problema donde la huella de la memoria de los navegadores crece lentamente con el tiempo. Esto está sucediendo tanto en Firefox como en IE 7 (aunque no en Chrome). Después de unas horas, puede provocar que IE7 tenga una huella de ~ 200MB y FF3 para tener una huella de ~ 400MB.

Después de muchas pruebas, he encontrado que la pérdida de memoria solo ocurre si se responden las llamadas AJAX. Si el servidor no responde a nada, puedo dejar la página abierta durante horas y la huella no crecerá.

Estoy usando un prototipo para mis llamadas AJAX. Entonces, supongo que hay un problema con la devolución de llamada onSuccess que crea estas pérdidas de memoria.

¿Alguien tiene alguna sugerencia para evitar fugas de memoria con el prototipo/AJAX? ¿O algún método sobre cómo solucionar este problema?

EDIT: descubrí que el problema radica en una biblioteca de gráficos js que estoy usando. Se puede ver here.

Respuesta

10

Lo más importante que puede observar es los eventos y cómo los asigna.

Por ejemplo, tomemos este escenario (ya que no se ha proporcionado uno):

<div id="ajaxResponseTarget"> 
    ... 
</div> 
<script type="text/javascript"> 
    $(someButton).observe('click', function() { 
     new Ajax.Updater($('ajaxResponseTarget'), someUrl, { 
      onSuccess: function() { 
       $$('#ajaxResponseTarget .someButtonClass').invoke('observe', 'click', function() { 
        ... 
       }); 
      } 
     }); 
    }); 
</script> 

Esto creará una pérdida de memoria, ya que cuando se actualiza #ajaxResponseTarget (internamente, Prototipo utilizará innerHTML) con elementos de click los eventos se eliminarán del documento sin que se eliminen sus eventos. La segunda vez que haga clic en someButton, tendrá el doble de controladores de eventos y la recolección de elementos no utilizados eliminará el primer conjunto.

Una manera de evitar esto es usar delegación de eventos:

<div id="ajaxResponseTarget"> 
    ... 
</div> 
<script type="text/javascript"> 
    $('ajaxResponseTarget').observe('click', function(e) { 
     if(e.element().match('.someButtonClass')) { 
      ... 
     } 
    }); 
    $(someButton).observe('click', function() { 
     new Ajax.Updater($('ajaxResponseTarget'), someUrl); 
    }); 
</script> 

Debido a la forma en eventos DOM trabajo, el "clic" en la .someButtonClass se disparará también en #ajaxResponseTarget y Prototipo hace muerta simple para determinar qué elemento era el objetivo del evento. No se asignan eventos a los elementos dentro de#ajaxResponseTarget, por lo que no hay forma de reemplazar su contenido a los eventos huérfanos de los destinos dentro de.

+0

+1 Buena información, aunque nunca crearé ningún evento nuevo después de la carga de la página inicial. Todavía estoy tratando de descubrir qué estoy haciendo que podría causar el problema. –

+1

Si no está creando ningún evento ... ¿por qué aceptó esto como su respuesta? – Sean

-3

Puedo estar equivocado, pero parece que estás creando cierres alrededor del objeto de respuesta. Cada objeto de respuesta será diferente, lo que resulta en una mayor huella de memoria.

+0

¿Podría explicar un poco? Lo siento, no estoy seguro de lo que quiere decir con "cierres alrededor del objeto de respuesta". ¿Te refieres a una función? –

+0

http://en.wikipedia.org/wiki/Closure_(computer_science) –