Javascript内存泄漏/性能问题?

时间:2010-05-31 01:41:13

标签: javascript dom memory

我只是不能为我的生活弄清楚Internet Explorer中的内存泄漏。

insertTags simple接受字符串str,并将每个单词放在HTML的开始和结束标记内(通常是锚标记)。 transliterate用于阿拉伯数字,并用&#.. n替换正常数字0-9;他们的阿拉伯语对应的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次(古兰经中最长的surah 2中的经文#)。

* IE7和IE8中的内存泄漏,不确定大约6,但在Safari 4,FF 3.6,Opera 10.5,Chrome 5中完美运行...... *

4 个答案:

答案 0 :(得分:6)

变量的范围是函数,而不是/ else / for / while / etc。块。每次打电话

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

您正在创建一个新功能(新范围)。此新函数引用iresponse变量。所以现在,iresponse的范围是外部函数和新函数。

这还不足以泄漏内存。 (iresponse是在新函数完成后超出范围的正常变量)

但是,你在新函数中创建一个p DOM元素,并在外部函数中引用它(将它作为参数返回到fragment.appendChild调用)。现在考虑一下:你有外部作用域fragment引用从内部作用域创建的p DOM,它需要使用外部作用域中的iresponse变量首先创建DOM元素。

fragmentp DOM对象各自具有相互引用。尽管您尝试通过将变量指针置零来将引用计数置零,但p=nullfragment = 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 );

答案 1 :(得分:2)

虽然答案已被接受,但我认为这也可以完成这项工作:

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正在使用它

因此解决方案可以像提供词汇范围一样简单,如上所示

答案 2 :(得分:0)

如果从链接中删除onclick属性,它是否会泄漏?

您可以尝试删除重复的onclick并将其替换为事件委派。

此外,您的所有变量似乎都在全局范围内 - 这不应该导致您看到的问题,但是无论如何都应该解决这个问题。

答案 3 :(得分:-2)

我无法告诉你为什么 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引擎上可能需要一段时间。