【问题标题】:Simple jQuery Ajax call leaks memory in Internet Explorer简单的 jQuery Ajax 调用在 Internet Explorer 中泄漏内存
【发布时间】:2011-01-26 14:36:33
【问题描述】:

我创建了一个每秒进行一次 Ajax 调用的网页。在 Internet Explorer 7 中,它会严重泄漏内存(大约 15 分钟 20 MB)。

程序非常简单。它只是运行一个进行 Ajax 调用的 JavaScript 函数。服务器返回一个空字符串,JavaScript 代码对此不做任何事情。我使用setTimeout 每秒运行一次函数,我使用Drip 来观看。

这里是来源:

<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>

如何堵住这个漏洞?我有一个真正的应用程序可以通过这种方式更新一个大表,但如果无人看管它会占用千兆字节的内存。

编辑:好的,所以在一些好的建议之后,我将代码修改为:

<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>

不过,这似乎没有任何区别。我没有对 DOM 做任何事情,如果我注释掉 Ajax 调用,内存泄漏就会停止。所以看起来泄漏完全在 Ajax 调用中。 jQuery Ajax 是否天生就创建了某种循环引用,如果是,我该如何释放它?顺便说一句,它不会在 Firefox 中泄漏。

有人建议在另一个 VM 中运行测试,看看结果是否相同。我没有设置另一个 VM,而是找到了一台运行 XP Home 和 Internet Explorer 8 的笔记本电脑。它也出现了同样的问题。

我尝试了一些旧版本的 jQuery 并获得了更好的结果,但直到我在 jQuery 中放弃 Ajax 并使用更传统(且丑陋)的 Ajax 后,问题才完全消失。

【问题讨论】:

  • 对答案也很好奇——我有想法但想知道结果。
  • 好问题,我不知道答案,但您可能会考虑在成功函数中设置 setTimeout,这样如果您开始发出需要时间的请求,就不会导致服务器超载。如果您在发送下一个请求之前没有从服务器获得全部响应,您将打开一个新连接。
  • 感谢关于移动 setTimeout 的建议。我试过了,但没有用。
  • 您是否对可能导致问题的结果进行了任何处理?
  • 我没有对结果做任何事情。除了url,我正在运行上面列出的程序。

标签: jquery ajax memory-leaks


【解决方案1】:

如果你在 javascript 中使用 setinterval 并且没有正确清除它,计时器可能会启动多次,导致调用堆栈。

试试类似的东西

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

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

【讨论】:

    【解决方案2】:

    我自己也遇到过。最初我认为这与 UI 库有关,但在我换成 jQuery 1.5 后它就消失了。对于我正在使用的 1.4.2 版本。 (1.4.4 似乎没有解决问题)。

    【讨论】:

      【解决方案3】:

      这是 jQuery 上的错误的 link,以及作为 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;
                                  }
                          }
                  };
      

      注意:这已在 jQuery 1.4.4 中正式修复,因此最好的办法是立即升级。

      【讨论】:

      • 1.4.4 没有为我修复它。 1.5 确实
      • 我在 jQuery UI 1.8.5 中使用此修复程序时遇到了问题,收到 JS 错误“xhr.abort is not a function”。
      • @iqnivek - 你最好的选择是升级到最新的 jquery (1.5.1)。此处建议的特定修复并不完美。
      【解决方案4】:

      问题似乎出在 Internet Explorer 中的 jQuery 1.4 上,在较小程度上也出在 1.2 和 1.3 版本上。

      1.4.0、1.4.1 和 1.4.2 都表现出严重的内存泄漏。

      1.2.3、1.2.6、1.3.0、1.3.1 和 1.3.2 都表现出更小的泄漏(10 分钟后大约 100 KB)。

      我还尝试了一个以更传统的方式调用 Ajax 的程序版本:

      <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>
      

      这完全消除了泄漏。

      所以看起来我将不得不重复使用丑陋的旧方式调用 Ajax,直到 jQuery 人员解决这个问题。

      【讨论】:

      • 是的,如果您想继续以 jQuery 方式做事,请使用我回答中的补丁...实际上必须直接管理您的 XHR 内容很糟糕!
      • 有趣的是,在发布时,我们在当前的 JQuery 1.6.2 中似乎仍然存在问题。多次调用 .getJSON 会消耗越来越多的内存,直到您终止该进程,即使您对内容不执行任何操作。 stackoverflow.com/questions/6752335/…
      【解决方案5】:

      我已经看到了,我不相信这本身就是内存泄漏。只是Ajax请求因为缓存没有返回数据。

      添加故意缓存控制,如:

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

      这是东西落入裂缝的事情之一,也就是说,使用缓存和 XMLHttpRequest 做特定的事情,而 jQuery 默认情况下不使用缓存。

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题,整个早上都被难住了……直到几分钟前。问题是在您设置onreadystatechange 处理程序时创建的循环引用,IE 不够聪明,无法中断。因此,解决方案是明确打破它。但是,显然您不能在处理程序本身中执行此操作(尽管如果可以的话会很方便!)。

        魔法声明:

        delete request['onreadystatechange'];
        

        您需要记录您为其设置onreadystatechange 的每个XMLHttpRequest 对象。然后,在readyState 变为 4 之后的某个时刻,对对象施展魔法。如果您已经在执行重复的 AJAX 轮询,则检查清理请求的逻辑位置将在同一个轮询循环中。我定义了一个简单的RequestTracker 对象来管理我的请求。

        这对我有用;我证实它解决了泄漏。这是一个特别引路的链接(我会发布更多,但 StackOverflow 不允许我):

        【讨论】:

          【解决方案7】:

          您的代码的一个问题是,如果您的 ajax 请求开始需要一段时间,您将开始用 ajax 请求淹没浏览器和服务器,您真的应该等到浏览器从服务器获得返回后再开始下一个请求。

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

          【讨论】:

          • @petersendidit - 最好阅读有关问题的 cmets :)
          • 谢谢。我进行了更改,但我没有注意到有什么不同。
          【解决方案8】:

          eval() 肯定会占用内存(eval 发生在将字符串传递给 setTimeout 进行评估时),不要在测试中使用它:

          setTimeout('testJunk()',1000);
          

          应该是:

          setTimeout(testJunk, 1000);
          

          另外一个更好的用法是setInterval() 用于您想要的重复操作,试试这个:

          setInterval(testJunk, 1000);
          

          【讨论】:

          • 感谢您的建议。我尝试过这个。如果它有所作为,那是很小的。内存还在增加。
          • @Thomas - 这是您问题中的整个页面,还是只是一个缩写版本,完整版中还有更多内容?
          • 我正在运行上面列出的程序,除了我修改了列表的 url。
          • @Thomas - 我让您的示例在 2 个不同的 IE7 虚拟机中运行了过去半小时...内存使用量上升了几兆,然后看起来像垃圾收集启动并将其击倒。我会在另一台机器上测试,最好得到第三个样本。
          • 尼克 - 有趣。就我而言,这似乎并不是缺少垃圾收集。我在 testJunk 函数中添加了对“CollectGarbage”的调用,但没有帮助。我在运行 IE6 的不同 VM 上运行它,并且那里没有内存泄漏。我打算在一个带有 IE8 的系统上试一试,看看会发生什么。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多