Простой jQuery Ajax вызывает утечку памяти в Internet Explorer - PullRequest
40 голосов
/ 12 марта 2010

Я создал веб-страницу, которая делает вызов 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.

Ответы [ 8 ]

19 голосов
/ 08 апреля 2010

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

8 голосов
/ 12 марта 2010

Проблема, по-видимому, связана с 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 не решат эту проблему.

7 голосов
/ 02 апреля 2010

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

Волшебное утверждение:

delete request['onreadystatechange'];

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

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

5 голосов
/ 12 марта 2010

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

setTimeout('testJunk()',1000);

должно быть:

setTimeout(testJunk, 1000);

Также лучше использовать в целом setInterval() для повторной операции, как вы хотите, попробуйте это:

setInterval(testJunk, 1000);
0 голосов
/ 25 февраля 2014

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

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

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

function clear() { 
    clearInterval(myVar); 
    GetData("ServiceLibrary","GetCalls",sdata,Complete);
};
0 голосов
/ 21 февраля 2011

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

0 голосов
/ 08 октября 2010

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

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

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

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

0 голосов
/ 12 марта 2010

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

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