2010-03-11 10 views
40

Creé una página web que hace una llamada Ajax cada segundo. En Internet Explorer 7, pierde memoria mal (20 MB en unos 15 minutos).Llamada jQuery Ajax simple pierde memoria en Internet Explorer

El programa es muy simple. Simplemente ejecuta una función de JavaScript que realiza una llamada Ajax. El servidor devuelve una cadena vacía y el código JavaScript no hace nada con ella. Yo uso setTimeout para ejecutar la función cada segundo, y estoy usando Drip para ver la cosa.

Aquí está la fuente:

<html> 
    <head> 
    <script type="text/javascript" src="http://www.google.com/jsapi"></script> 
    <script type="text/javascript"> 
     google.load('jquery', '1.4.2'); 
     google.load('jqueryui', '1.7.2'); 
    </script> 
    <script type="text/javascript"> 
     setTimeout('testJunk()',1000); 
     function testJunk() { 
     $.ajax({ url: 'http://xxxxxxxxxxxxxx/test', // The url returns an empty string 
       dataType: 'html', 
       success: function(data){} 
       }); 
     setTimeout('testJunk()',1000) 
     } 
    </script> 
    </head> 
    <body> 
    Why is memory usage going up? 
    </body> 
</html> 

Cómo conectar esta filtración? Tengo una aplicación real que actualiza una gran mesa de esta manera, pero sin supervisión, consumirá gigabytes de memoria.

Editar: bien, así que después de algunas buenas sugerencias, he modificado el código para:

<html> 
    <head> 
    <script type="text/javascript" src="http://www.google.com/jsapi"></script> 
    <script type="text/javascript"> 
     google.load('jquery', '1.4.2'); 
     google.load('jqueryui', '1.7.2'); 
    </script> 
    <script type="text/javascript"> 
     setTimeout(testJunk,1000); 
     function testJunk() { 
     $.ajax({ url: 'http://xxxxxxxxxxxxxx/test', // The url returns an empty string 
       dataType: 'html', 
       success: function(data){setTimeout(testJunk,1000)} 
       }); 
     } 
    </script> 
    </head> 
    <body> 
    Why is memory usage going up? 
    </body> 
</html> 

No parecía hacer ninguna diferencia, sin embargo. No estoy haciendo nada con el DOM, y si hago un comentario sobre la llamada Ajax, la pérdida de memoria se detiene. Entonces parece que la fuga está completamente en la llamada de Ajax. ¿Inherentemente jQuery Ajax crea algún tipo de referencia circular, y si es así, cómo puedo liberarlo? Por cierto, no se filtra en Firefox.

Alguien sugirió ejecutar la prueba en otra VM y ver si los resultados son los mismos. En lugar de configurar otra máquina virtual, encontré una computadora portátil que ejecutaba XP Home con Internet Explorer 8. Presenta el mismo problema.

Probé algunas versiones anteriores de jQuery y obtuve mejores resultados, pero el problema no desapareció por completo hasta que abandoné Ajax en jQuery y fui con el Ajax más tradicional (y feo).

+0

también muy curiosos acerca de la respuesta - Tengo ideas pero quiero saber los resultados. – Plynx

+0

Buena pregunta, no sé la respuesta, pero podría considerar tener el setTimeout en la función de éxito para que no termine sobrecargando el servidor si comienza a hacer solicitudes que llevan tiempo. En caso de que no hayas recibido toda la respuesta del servidor antes de enviar la siguiente solicitud, abrirás una nueva conexión. – MyGGaN

+0

Gracias por la sugerencia de mover setTimeout. Lo intenté, pero no ayudó. –

Respuesta

8

El problema parece ser con jQuery 1.4 en Internet Explorer, y en menor medida, las versiones 1.2 y 1.3.

1.4.0, 1.4.1 y 1.4.2 todos exhibieron la pérdida de memoria grave.

1.2.3, 1.2.6, 1.3.0, 1.3.1 y 1.3.2 mostraron una fuga mucho menor (alrededor de 100 KB después de 10 minutos).

También probé una versión de mi programa que llama Ajax en una forma más tradicional:

<html> 
    <head> 
    <script language="javascript" type="text/javascript"> 
     function getHTTPObject() { 
     var xmlhttp; 
     /*@cc_on 
     @if (@_jscript_version >= 5) 
      try { 
      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); 
      } catch (e) { 
      try { 
       xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 
      } catch (E) { 
       xmlhttp = false; 
      } 
      } 
     @else 
     xmlhttp = false; 
     @end @*/ 
     if (!xmlhttp && typeof XMLHttpRequest != 'undefined') { 
      try { 
      xmlhttp = new XMLHttpRequest(); 
      if (xmlhttp.overrideMimeType) { 
       xmlhttp.overrideMimeType("text/xml"); 
      } 
      } catch (e) { 
      xmlhttp = false; 
      } 
     } 
     return xmlhttp; 
     } 
     var ajaxObject = getHTTPObject(); 
     setTimeout(testJunk,1000); 
     function testJunk() { 
     ajaxObject.open('POST', 'http://XXXXXXXXXXXXXXX/delme2', true); 
     ajaxObject.onreadystatechange = handleAjaxResponse; 
     ajaxObject.send(null); 
     } 
     function handleAjaxResponse() { 
     if (ajaxObject.readyState==4) { 
      setTimeout(testJunk,1000); 
     } 
     } 
    </script> 
    </head> 
    <body> 
    <div id="test">Why is memory usage going up?</div> 
    </body> 
</html> 

Esto se deshizo de la fuga por completo.

Parece que tendré que repetir mis repetidas llamadas a Ajax de forma fea hasta que la gente de jQuery solucione este problema.

+1

Sí, si quieres seguir haciendo las cosas al estilo jQuery, usa el parche en mi respuesta ... ¡En realidad, tener que administrar tus cosas XHR es una mierda! – Ryley

+0

curiosamente, parece que todavía tenemos un problema en el JQuery actual, 1.6.2 en el momento de la publicación. Las llamadas múltiples a .getJSON consumen más y más memoria hasta que mata el proceso, incluso si no hace nada para contentarlo. http://stackoverflow.com/questions/6752335/memory-leak-when-pulling-json-from-web –

5

eval() se comen la memoria con seguridad (eval sucede cuando se pasa una cadena para evaluar SetTimeOut), no lo use en las pruebas:

setTimeout('testJunk()',1000); 

debería ser:

setTimeout(testJunk, 1000); 

también un mejor uso en general sería setInterval() para una operación repetida como usted quiera, intente esto:

setInterval(testJunk, 1000); 
+0

Gracias por la sugerencia. Lo intenté. Si hacía una diferencia, era pequeño. La memoria todavía está subiendo. –

+0

@Thomas - ¿Es esa toda la página en su pregunta, o solo una versión abreviada y más está sucediendo en la versión completa? –

+0

Estoy ejecutando el programa como se indica anteriormente, excepto que he destruido la URL del listado. –

0

Un problema con su código es que si su solicitud de ajax comienza a tomar un tiempo, comenzará a inundar el navegador y el servidor con una solicitud de ajax, realmente debería esperar hasta que el navegador obtenga un retorno del servidor antes de iniciar el siguiente.

function testJunk() { 
    $.ajax({ url: 'http://xxxxxxxxxxxxxx/test', // The url returns an empty string 
     dataType: 'html', 
     complete: function(data){ 
      setTimeout(testJunk,1000); 
     } 
    }); 
} 
testJunk(); 
+0

@petersendidit - Mejor leer los comentarios sobre la pregunta :) –

+0

Gracias. Hice el cambio, pero no noté la diferencia. –

7

Me encontré con el mismo problema y había quedado perplejo toda la mañana ... hasta hace unos momentos. El problema es una referencia circular que se crea cuando configura el controlador onreadystatechange, que IE no es lo suficientemente inteligente como para romperse. La solución, por lo tanto, es romperla explícitamente. Sin embargo, obviamente no puedes hacerlo desde el controlador mismo (¡aunque sería conveniente si pudieras!).

La declaración mágica:

delete request['onreadystatechange']; 

Es necesario mantener un registro de cada XMLHttpRequest objeto para el que se establece onreadystatechange. Luego, en algún momento después de que readyState pase a 4, haz tu magia en el objeto. Si ya está realizando una encuesta AJAX repetida, el lugar lógico para verificar las solicitudes de limpieza estaría en el mismo ciclo de sondeo. Definí un objeto simple RequestTracker para administrar mis solicitudes.

Esto funcionó para mí; Verifiqué que resolvió la fuga. Aquí hay un enlace en particular, que abrió el camino (que iba a publicar más, pero stackoverflow no me está dejando):

+1

+1 Gran investigación, esto seguramente será útil. – Plynx

19

Aquí hay una link al error más en jQuery, junto con este como una solución sugerida para jQuery 1.4.2:

--- jquery-1.4.2.js  2010-04-08 12:10:20.000000000 -0700 
+++ jquery-1.4.2.js.fixed  2010-04-08 12:10:38.000000000 -0700 
@@ -5219,7 +5219,7 @@ 

          // Stop memory leaks 
          if (s.async) { 
-          xhr = null; 
+          xhr.onreadystatechange = null; xhr.abort = null; xhr = null; 
          } 
        } 
      }; 

NOTA: Esta se fija oficialmente en jQuery 1.4.4, así que lo mejor es simplemente actualizar ahora.

+2

Esta debería ser la respuesta aceptada. +1 –

+0

1.4.4 no lo arregló para mí. 1.5 lo hace aunque –

+0

He tenido problemas al usar esta corrección con jQuery UI 1.8.5, obteniendo el error JS "xhr.abort no es una función". –

0

He visto esto, y no creo que sea una pérdida de memoria per se. Es solo que la solicitud de Ajax vuelve sin datos debido al almacenamiento en caché.

Añadir control de caché deliberada, como en:

$.ajax({ url: 'http://xxxxxxxxxxxxxx/test', // The url returns an empty string 
     dataType: 'html', 
     cache: false, 
     success: function(data){} 
    }); 
    setTimeout('testJunk()',1000) 

Es una de esas cosas donde las cosas se cae por las grietas, es decir, hace una cosa específica con almacenamiento en caché y XMLHttpRequest y jQuery no utiliza caché fuera por defecto.

0

Acabo de encontrar esto yo mismo. Pensé que era algo que tenía que ver con la biblioteca de UI inicialmente, pero luego desapareció después de que intercambié jQuery 1.5. para la versión 1.4.2 que estaba usando. (1.4.4 no pareció solucionar el problema).

0

si está utilizando el valor de setinterval en javascript y no lo borra correctamente, el temporizador puede iniciarse varias veces, causando una pila de llamadas.

intentar algo así como

var myVar = setInterval(function() { clear() }, 5000); 

function clear() { 
    clearInterval(myVar); 
    GetData("ServiceLibrary","GetCalls",sdata,Complete); 
}; 
Cuestiones relacionadas