Утечка памяти Javascript / проблема с производительностью? - PullRequest
2 голосов
/ 31 мая 2010

Я просто не могу на всю жизнь понять эту утечку памяти в Internet Explorer.

insertTags simple принимает строку str и помещает каждое слово в начальный и конечный теги для HTML (обычно это теги привязки). transliterate для арабских чисел и заменяет нормальные числа 0-9 на a & # .. n; Идентификация XML для их арабских аналогов.

fragment = document.createDocumentFragment();
for (i = 0, e = response.verses.length; i < e; i++)
{
    fragment.appendChild((function(){
        p = document.createElement('p');
        p.setAttribute('lang', (response.unicode) ? 'ar' : 'en');
        p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>');
        try { return p } finally { p = null; }
    })());
}
params[0].appendChild( fragment );
fragment = null;

Мне бы очень понравились некоторые ссылки, кроме MSDN и about.com, потому что ни одна из них не объяснила мне, почему мой сценарий теряет память. Я уверен, что это проблема, потому что без этого все работает быстро (но ничего не отображается).

Я читал, что выполнение многих манипуляций с DOM может быть опасным, но цикл for не более 286 раз (количество стихов в суре 2, самой длинной суре в Коране).

* Утечки памяти в IE7 и IE8, не уверен насчет 6, но отлично работает в Safari 4, FF 3.6, Opera 10.5, Chrome 5 ... *

Ответы [ 4 ]

6 голосов
/ 31 мая 2010

Переменные ограничены функциями, а не если / else / for / while / etc. блоки. Каждый раз, когда вы звоните

fragment.appendChild((function() { ...

вы создаете новую функцию (новую область видимости). Эта новая функция ссылается на переменные i и response. Так что теперь i и response ограничены ОБА внешней функцией и новой функцией.

Этого недостаточно для утечки памяти. (i и response - это обычные переменные, которые выйдут из области видимости после выполнения новой функции)

НО, вы создаете элемент p DOM в новой функции и ссылаетесь на него во внешней функции (вы возвращаете его в вызов fragment.appendChild в качестве аргумента). Теперь подумайте об этом: у вас есть внешняя область действия fragment, ссылающаяся на p DOM, созданный из внутренней области действия, для которой нужно было использовать переменные i и response из внешней области действия, чтобы создать элемент DOM в первом место.

Объекты fragment и p DOM имеют ссылку друг на друга. Несмотря на ваши попытки обнулить счетчик ссылок путем обнуления указателей переменных, p=null и fragment = null не избавят от всех ссылок. У fragment все еще есть ссылка на внутреннюю p, которая все еще имеет ссылку на внешнюю переменную response. Из-за этой оставшейся циклической зависимости две «области видимости» никогда не будут собираться мусором.

Кто-нибудь, пожалуйста, исправьте меня, если я допустил какие-либо ошибки.


Что касается решения, просто не используйте внутреннюю функцию!

fragment = document.createDocumentFragment();
for (var i = 0, var e = response.verses.length; i < e; i++)
{
    var p = document.createElement('p');
    p.setAttribute('lang', (response.unicode) ? 'ar' : 'en');
    p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>');
    fragment.appendChild(p);
}
params[0].appendChild( fragment );
2 голосов
/ 03 января 2013

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

var fragment = document.createDocumentFragment();

for (var i = 0, e = response.verses.length; i < e; i++) {
    fragment.appendChild((function(p){ // Create a scope here itself :)
        p = document.createElement('p'); // ?? without above, it was a global scope
        p.setAttribute('lang', (response.unicode) ? 'ar' : 'en');
        p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>');
        try { return p } finally { p = null; }
    })());
}
params[0].appendChild( fragment );
fragment = null;

Причина утечки памяти: замыкание создается и возвращается в анонимной функции, а затем сохраняется, но не сборщик мусора , поскольку fragment использует его

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

0 голосов
/ 31 мая 2010

Утечка, если вы удалите атрибут onclick из ссылки?

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

Кроме того, все ваши переменные, похоже, находятся в глобальной области видимости - это не должно быть так плохо, чтобы вызвать проблемы, с которыми вы сталкиваетесь, но вы должны исправить это независимо.

0 голосов
/ 31 мая 2010

Я не могу сказать вам , почему IE предположительно теряет память, но этот код чертовски сложен для того, что он делает И эта строка кажется крайне подозрительной и лишней: try { return p } finally { p = null; }.

Как насчет упрощения и определения области действия переменных:

var fragment = document.createDocumentFragment();
var p, t;
for (var i = 0; i < response.verses.length; i++)
{
    p = document.createElement('p');
    if (response.unicode) {
        p.setAttribute('lang', 'ar');
        t = (response.surah + ':' + (i+1)).transliterate();
    } else {
        p.setAttribute('lang', 'en');
        t = response.surah + ':' + (i+1);
    }
    p.innerHTML = t + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>');
    fragment.appendChild(p);
}
params[0].appendChild(fragment);
fragment = p = t = null;  // likely unnecessary if they go out of scope anyway

Это все еще много операций DOM, которые могут занять некоторое время на медленных движках Javascript.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...