Простой jQuery Ajax вызывает утечку памяти в Internet Explorer

Я создал веб-страницу, которая делает вызов Ajax каждую секунду. В Internet Explorer 7 он плохо протекает в памяти (20 МБ за 15 минут).

Программа очень проста. Он просто запускает функцию JavaScript, которая вызывает вызов Ajax. Сервер возвращает пустую строку, а код 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.

Кто-то предложил запустить тест в другой виртуальной машине и посмотреть, совпадают ли результаты. Вместо того, чтобы настраивать другую виртуальную машину, я обнаружил, что ноутбук, на котором работает XP Home с Internet Explorer 8. Он обнаружил ту же проблему.

Я попробовал несколько старых версий jQuery и получил лучшие результаты, но проблема не исчезла полностью, пока я не оставил Ajax в jQuery и пошел с более традиционным (и уродливым) Ajax.

Ответ 1

Проблема связана с jQuery 1.4 в Internet Explorer и, в меньшей степени, версиями 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 все показали гораздо меньшую утечку (около 100 КБ через 10 минут).

Я также попробовал версию своей программы, которая вызывает 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 не сгладят эту проблему.

Ответ 2

Здесь ссылка с ошибкой в ​​jQuery, вместе с этим в качестве предлагаемого исправления для 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, поэтому лучше всего обновить его сейчас.

Ответ 3

Я столкнулся с той же проблемой и был весь день в тупике... до нескольких мгновений назад. Проблема заключается в циклической ссылке, которая создается, когда вы устанавливаете обработчик onreadystatechange, что IE недостаточно умен, чтобы ломаться. Поэтому решение состоит в том, чтобы разбить его явно. Однако, очевидно, вы не можете сделать это из самого обработчика (хотя было бы удобно, если бы вы могли!).

Магическое утверждение:

delete request['onreadystatechange'];

Вам нужно сохранить запись каждого объекта XMLHttpRequest, для которого вы установили onreadystatechange. Затем, в какой-то момент после readyState переходит в 4, сделайте свою магию на объекте. Если вы уже проводите повторный опрос AJAX, логическое место для проверки запросов на очистку будет в том же цикле опроса. Я определил простой объект RequestTracker для управления моими запросами.

Это сработало для меня; Я проверил, что он решил утечку. Здесь одна ссылка, в частности, которая вела путь (я бы опубликовал больше, но StackOverflow не позволяет мне):

Ответ 4

eval() наверняка доберется до памяти (eval происходит при передаче строки в значение setTimeout), не используйте ее при тестировании:

setTimeout('testJunk()',1000);

должен быть:

setTimeout(testJunk, 1000);

Лучше всего использовать setInterval() для повторной операции, как вы хотите, попробуйте следующее:

setInterval(testJunk, 1000);

Ответ 5

Одна проблема с вашим кодом заключается в том, что если ваш запрос ajax начнет занять некоторое время, вы начнете наводнение браузера и сервера с помощью запроса ajax, вам действительно стоит подождать, пока браузер не получит возврат с сервера, прежде чем начать следующий.

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

Ответ 6

Я видел это, и я не считаю, что это утечка памяти. Это просто, что запрос Ajax возвращается без данных из-за кэширования.

Добавьте преднамеренное управление кешем, как в:

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

Это одна из тех вещей, где материал падает на трещины, то есть делает что-то конкретное с кешированием и XMLHttpRequest, а jQuery по умолчанию не использует кеш.

Ответ 7

Просто это произошло. Я думал, что это как-то связано с библиотекой пользовательского интерфейса, но затем оно исчезло после того, как я поменялся на jQuery 1.5. для версии 1.4.2, которую я использовал. (1.4.4, похоже, не исправить проблему).

Ответ 8

если вы используете setinterval в javascript и не очищаете его должным образом, таймер может запускаться несколько раз, вызывая стек вызовов.

попробуйте что-то вроде

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

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