утечка памяти в JavaScript в IE6 с помощью базового плагина jquery ui - PullRequest
4 голосов
/ 15 февраля 2011

Во-первых, я много читал и исследовал эту проблему. Уже есть билет с jquery UI , за которым я следую. И я знаю способ обойти эту ошибку, но мне очень любопытно, почему это происходит. Я полагаю, что ошибка связана с закрытием, но мой javascript-fu не эксперт.

Я полагаю, что у команды jquery UI есть дела поважнее, чем тратить энергию на ошибку IE6. Поэтому я хотел донести это до широкой публики javascript.

Ниже приведен тестовый пример:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>jquery ui memory leak test</title>
    <script type="text/javascript" src="jquery-1.5.js"></script>
    <script type="text/javascript" src="jquery.ui.widget.js"></script>
    <script type="text/javascript">
    (function($) {    
        $.widget("ui.test", {
            _create: function() {
            }
        });

        $(document).ready(function() {
            for (var i = 0; i < 1; i++) {
                $("#container").append("<div id='inner'></div>");
                $("#inner").test();
                $("#inner").test("destroy");
                $("#container").empty();
            };
        });
    })(jQuery);
    </script>
</head>
<body>
    <div id="container">
    </div>
</body>
</html>

Я тестировал, используя все комбинации jquery 1.4.4 и 1.5 и jquery-ui-1.8.9 и jquery-ui master (на момент написания статьи), но все они дают одинаковый результат.

Мой тестовый виджет, я считаю, самый простой, который вы можете получить.

Если вы тестируете, используя sIEve , вы можете обнаружить утечку. В противном случае увеличьте счетчик до 1000, и вы увидите, что объем памяти увеличится довольно легко. Существует еще один инструмент от Microsoft, который вы также можете использовать для обнаружения утечек.

Таким образом, утечка связана с пользовательской привязкой события в методе _createWidget виджета:

var self = this;
this.element.bind( "remove." + this.widgetName, function() {
    self.destroy();
});

Так что, если я это прокомментирую, утечки нет. Я использую 1.8.9, а не master, так как код виджета 1.8.9 кажется проще (master немного изменился).

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

$("#inner").bind("remove.test", function() {});

Я специально добавил функцию no-op, но не имеет значения, что находится внутри функции обратного вызова. Вы можете поспорить, так как впоследствии я делаю уничтожение вручную, привязка не нужна. Но дело не в этом.

Итак, мой вопрос: почему происходит утечка исходного кода, вызова привязки из кода виджета? Я подозреваю, что это из-за закрытия, но я не могу это объяснить.

Может кто-нибудь объяснить это?

Ответы [ 2 ]

2 голосов
/ 15 февраля 2011

Насколько я понимаю, проблема возникает, когда существует циклическая ссылка между JS и DOM, когда переменная JS указывает на объект DOM, и этот объект DOM имеет свойство (или, как правило, обработчик события), указывающее обратно на переменная JS. Ваш пример выше с .bind (), кажется, делает это. Очевидно, IE использует подсчет ссылок в процессе сборки мусора, а циклические ссылки не собираются.

Я видел по крайней мере пару блогов MSDN, которые обвиняли это в JavaScript и в основном рекомендовали избегать закрытий, что, очевидно, не очень полезно, но вот некоторые статьи, не относящиеся к Microsoft / MSDN, которые обсуждают проблему и дают некоторые обходные пути. :

http://laurens.vd.oever.nl/weblog/items2005/closures/

http://www.ibm.com/developerworks/web/library/wa-memleak/

Закрытие JavaScript и утечки памяти

0 голосов
/ 20 марта 2012

Мне жаль, что я не могу дать объяснение причины этой проблемы, но я работал над обходным путем, чтобы предотвратить эту довольно раздражающую утечку.

После добавления jquery и jquery-ui на мою страницу я добавил следующий код:

var origCreateWidget = $.Widget.prototype._createWidget;
$.Widget.prototype._createWidget = function( options, element ) {
   var origBind = $.fn.bind;
   var widget = this;
   $.fn.bind = function( type, func ) {
      if( typeof( type ) === "string" && type.indexOf( "remove." ) === 0) {
         // ignore the remove events
      }
      else {
         origBind.apply( this, arguments );
      }
      return this;
   }

   var res = origCreateWidget.call( this, options, element );

   $.fn.bind = origBind;
   return res;
};

Дополнительно я вызываю destroy, когда происходит событие выгрузки окна. После этих изменений sIEve не сообщает о каких-либо утечках, а диспетчер задач Windows показывает постоянное потребление памяти при навигации по страницам, содержащим сортируемый виджет.

После нескольких часов поисков и испытаний я так и не смог выяснить истинную причину утечки.

...