javascript js - Ошибка утечки памяти при закрытии JavaScript




1 Answers

Раньше я работал с экс-программным менеджером для JavaScript внутри Microsoft, на не-браузерном проекте EcmaScript (err .. JScr ... JavaScript). У нас были длительные дискуссии о закрытии. В конце концов, дело в том, что их сложнее GC, а не невозможно. Мне пришлось бы прочитать дискуссию DC о том, как MS «ошибается», что замыкания вызывают утечку памяти. В старых версиях IE закрытие было, конечно, проблематичным, потому что было очень сложно собрать мусор с реализацией MS . Мне кажется странным, что парень Yahoo попытается рассказать архитекторам MS, что известная проблема с их кодом была где-то в другом месте. Насколько я ценю его работу, я не понимаю, на каком основании он был там.

Помните, статья, о которой вы ссылаетесь выше, относится к IE6, поскольку IE7 все еще находился под сильным развитием на момент написания статьи.

В стороне - слава богу, IE6 мертв (не заставляйте меня раскошеливаться на похоронах). Хотя, не забывайте о своем наследии ... Я еще не видел, чтобы кто-нибудь сделал убедительный аргумент, что он не был лучшим браузером в мире на первый день его выпуска - проблема в том, что они выиграли войну браузера , И вот, в чем состоит один из больших промахов их истории - они уволили команду после этого, и она сидела застойной почти 5 лет. Команда IE была до 4 или 5 парней, которые исправляли ошибки в течение многих лет, создавая огромную утечку мозгов и резко отставая от кривой. К тому времени, когда они наняли команду и поняли, где они находятся, они были годами назад из-за того, что они снова занялись монотонной кодовой базой, которую никто в действительности не понимал. Это моя перспектива как внутренняя в компании, но не напрямую привязанная к этой команде.

Помните также, что IE никогда не оптимизировался для закрытия, потому что не было ProtoypeJS (черт возьми, не было Rails), а jQuery едва представлял собой проблеск в голове Resig.

На момент написания статьи они также по-прежнему ориентировались на машины с 256 мегабайтами оперативной памяти, что также не было окончанием жизни.

Я считал справедливым, чтобы передать вам этот урок истории, после того как я прочитал всю вашу книгу вопроса.

В конце концов, я хочу сказать, что вы ссылаетесь на материал, который очень датирован. Да, избегайте замыканий в IE6, поскольку они вызывают утечку памяти - но что не было в IE6?

В конце концов, это проблема, с которой MS обратилась и продолжает обращаться. Вы собираетесь сделать некоторый уровень закрытия, так и было, даже тогда.

Я знаю, что они делали тяжелую работу в этой области вокруг IE8 (так как мой неоспоримый проект использовал нестандартный JavaScript-движок), и эта работа продолжалась в IE9 / 10. StatCounter (http://gs.statcounter.com/) предлагает, чтобы IE7 снизился до 1,5% рынка, по сравнению с 6% год назад, а при разработке новых сайтов IE7 становится все менее актуальным. Вы также можете разработать для NetScape 2.0, который представил поддержку JavaScript, но это было бы чуть менее глупо.

На самом деле ... Не пытайтесь переопределить ради двигателя, которого больше нет.

head title

решаемая

В Интернете есть много противоречивой информации по этому вопросу. Благодаря @John мне удалось понять, что закрытие (как используется ниже) не является причиной утечки памяти, и что даже в IE8 они не так распространены, как утверждают люди. Фактически в моем коде произошла только одна утечка, что оказалось не так сложно исправить.

Отныне мой ответ на этот вопрос будет:
AFAIK, единственный раз утечка IE8, - это когда привязаны события / обработчики на глобальном объекте. ( window.onload , window.onbeforeunload , ...). Чтобы обойти это, см. Мой ответ ниже.

ОГРОМНОЕ ОБНОВЛЕНИЕ:

Теперь я полностью потерян ... Через некоторое время, пробираясь сквозь статьи и старая и новая, у меня осталось хотя бы одно огромное противоречие. Хотя один из авторов JavaScript Guru (Douglas Crockford) говорит:

Поскольку IE не может выполнять свою работу и восстанавливать циклы, нам приходится это делать. Если мы явно разрываем циклы, тогда IE сможет восстановить память. По словам Microsoft, закрытие является причиной утечки памяти. Это, конечно, глубоко неправильно, но это приводит к тому, что Microsoft дает очень плохие советы программистам о том, как справляться с ошибками Microsoft. Оказывается, легко разбить циклы на стороне DOM. Их практически невозможно разбить на стороне JScript.

И как @freakish отметил, что мои фрагменты ниже похожи на внутренние работы jQuery, я чувствовал себя довольно уверенно в своем решении, не вызывая утечки памяти. В то же время я нашел эту страницу MSDN , где раздел Circular References with Closures представлял для меня особый интерес. На рисунке ниже представлено схематическое представление о том, как работает мой код, не так ли:

Единственное различие заключается в том, что у меня есть здравый смысл не привязывать слушателей событий к самим элементам.
Все-таки Дугги совершенно недвусмысленно: закрытие не является источником mem-утечек в IE. Это противоречие не дает мне понять, кто прав.

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

Последнее : я также узнал, что IE управляет DOM за пределами механизма JScript, что заставляет меня беспокоиться, когда я меняю дочерние элементы элемента <select> на основе запроса ajax:

function changeSeason(e)
{
    var xhr,sendVal,targetID;
    e = e || window.event;//(IE...
    targetID = this.id.replace(/commonSourceFragment/,'commonTargetFragment');//fooHomeSelect -> barHomeSelect
    sendVal = this.options[this.selectedIndex].innerHTML.trim().substring(0,1);
    xhr = prepareAjax(false,(function(t)
    {
        return function()
        {
            reusableCallback.apply(this,[t]);
        }
    })(document.getElementById(targetID)),'/index/ajax');
    xhr({data:{newSelect:sendVal}});
}

function reusableCallback(elem)
{
    if (this.readyState === 4 && this.status === 200)
    {
        var data = JSON.parse(this.responseText);
        elem.innerHTML = '<option>' + data.theArray.join('</option><option>') + '</option>';
    }
}

Если IE действительно управляет DOM, как если бы механизм JScript не был там, каковы шансы, что элементы опций не будут освобождены с помощью этого кода?
Я специально добавил этот фрагмент в качестве примера, потому что в этом случае я передаю переменные, которые являются частью области закрытия, как аргумент глобальной функции. Я не мог найти документацию по этой практике, но на основании документации, предоставленной Miscrosoft, она должна разорвать любые циклические ссылки, которые могут возникнуть, не так ли?

Предупреждение : длительный вопрос ... ( извините )

Я написал несколько довольно больших JavaScripts, чтобы делать вызовы Ajax в моем веб-приложении. чтобы избежать множества обратных вызовов и событий, я полностью воспользовался делегированием и закрытием событий. Теперь я написал функцию, которая заставляет меня задуматься о возможных утечках памяти. Хотя я знаю, что IE> 8 сделок с закрытием намного лучше, чем его предшественники, это политика компании, чтобы поддерживать IE 8 все-таки.

Ниже я привел пример того, о чем я говорю, here вы можете найти аналогичный пример, хотя он не использует ajax, но setTimeout, результат почти такой же. (Вы можете, конечно, пропустить код ниже, самому вопросу)

Код, который я имею в виду, таков:

function prepareAjax(callback,method,url)
{
    method = method || 'POST';
    callback = callback || success;//a default CB, just logs/alerts the response
    url = url || getUrl();//makes default url /currentController/ajax
    var xhr = createXHRObject();//try{}catch etc...
    xhr.open(method,url,true);
    xhr.setRequestMethod('X-Requested-with','XMLHttpRequest');
    xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
    xhr.setRequestHeader('Accept','*/*');
    xhr.onreadystatechange = function()
    {
        callback.apply(xhr);
    }
    return function(data)
    {
        //do some checks on data before sending: data.hasOwnProperty('user') etc...
        xhr.send(data);
    }
}

Все довольно прямолинейные вещи, за исключением обратного вызова onreadystatechange . Я заметил некоторые проблемы с IE при привязке обработчика напрямую: xhr.onreadystatechange = callback; , следовательно, анонимная функция. Не знаю почему, но я нашел, что это самый простой способ заставить его работать.

Как я уже сказал, я использую много делегирования событий, поэтому вы можете представить, что может оказаться полезным получить доступ к фактическому элементу / событию, которое вызвало вызов ajax. Поэтому у меня есть обработчики событий, которые выглядят так:

function handleClick(e)
{
    var target,parent,data,i;
    e = e || window.event;
    target = e.target || e.srcElement;
    if (target.tagName.toLowerCase() !== 'input' && target.className !== 'delegateMe')
    {
        return true;
    }
    parent = target;
    while(parent.tagName.toLowerCase() !== 'tr')
    {
        parent = parent.parentNode;
    }
    data = {};
    for(i=0;i<parent.cells;i++)
    {
        data[parent.cells[i].className] = parent.cells[i].innerHTML;
    }
    //data looks something like {name:'Bar',firstName:'Foo',title:'Mr.'}
    i = prepareAjax((function(t)
    {
        return function()
        {
            if (this.readyState === 4 && this.status === 200)
            {
                //check responseText and, if ok:
                t.setAttribute('disabled','disabled');
            }
        }
    })(target));
    i(data);
}

Как вы можете видеть, onreadystatechange вызов onreadystatechange является возвращаемым значением функции, которая предоставляет ссылку на target элемент при вызове обратного вызова. Благодаря делегированию событий мне больше не нужно беспокоиться о событиях, которые могут быть связаны с этим элементом, когда я решил удалить его из DOM (что я иногда делаю).
Однако, на мой взгляд, объект вызова функции обратного вызова может оказаться слишком большим для IE JScript-движка и его сборщика мусора:

Событие ==> handler ==> prepareAjax - довольно нормальная последовательность вызовов, но аргумент обратного вызова:

[Anon. func (аргумент t = target) возвращает anon. F (имеет доступ к t, который, в свою очередь, возвращается обратно к цели)]
===> передается функции обратного вызова anon, вызываемой с использованием метода .apply для объекта xhr, в свою очередь, частной переменной для функции prepareAjax

Я протестировал эту «конструкцию» в FF и хром. Он отлично работает там, но будет ли этот вид стоп-кода закрытия после закрытия при закрытии, причем каждый раз передача ссылки на элемент DOM является проблемой в IE (особенно версии до IE9)?

Нет, я не буду использовать jQuery или другие библиотеки. Мне нравится чистый JS, и я хочу знать как можно больше об этом серьезно недооцененном языке. Фрагменты кода не являются фактическими примерами копирования-вставки, но предоставляют IMO, хорошее представление о том, как я использую делегирование, закрытие и обратные вызовы на протяжении всего сценария. Поэтому, если какой-то синтаксис не совсем прав, не стесняйтесь его исправлять, но это не тот вопрос, о котором идет речь, конечно.




Related

javascript memory-leaks cross-browser closures