2010-05-31 57 views
2

我只是不能为我的生活弄清楚这个在Internet Explorer中的内存泄漏。Javascript内存泄漏/性能问题?

insertTags simple需要字符串str,并将每个单词放在HTML的开始和结束标记(通常是锚定标记)中。 transliterate代表阿拉伯数字,用&#代替正常数字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次(古兰经2中最古老的surah中的#节)。

*内存泄漏在IE7和IE8,不知道6,但在Safari 4的工作完全正常,FF 3.6,歌剧10.5,Chrome的5 ... *

+0

不知道你使用的是什么IE版本,但是我过去使用IE7有性能问题。在IE8/FF/Chrome上使用相同的代码很快,但IE7不太喜欢遍历DOM – 2010-05-31 02:03:21

回答

6

变量作用于函数,而不是/ else/for/while /等。块。每次拨打电话

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

同样的结论,更好的解释。我相信你*不应该在循环中使用'var p',因为你不应该重新声明一个现有的变量。 – deceze 2010-05-31 03:36:50

+0

循环内的var没有副作用。解析器将把这个声明提升到顶端。 声明在哪里使用的优势就在于它清楚了它在哪里使用。 – 2010-05-31 05:01:39

+0

谢谢,但是这导致了相同的结果。我使用这个代码,但发现内部函数实际上减轻了IE的负载!我只是不明白。 – Tom 2010-05-31 21:31:18

-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引擎一段时间。

+0

http://geekswithblogs.net/FrostRed/archive/2008/11/29/127440.aspx是我使用try {} finally {}的原因,它是一种预防内存泄漏的方法,它有助于从我在IE8中调试,但还不够。上面的代码也不相同。 – Tom 2010-05-31 02:05:03

+0

@Tom你说得对,我错误地拆解了你的三元操作符。修复。如果你没有使用函数来构造'p'元素,那么你就不应该像在那篇文章中描述的那样开始。 – deceze 2010-05-31 02:13:18

+1

@Tom:非常有用的帖子,谢谢。但它是第一个评论暗示什么可能是你的问题。看一看:****你会想修改你的代码。 IE有一个bug,除非你提供一个catch,否则它不会执行你的finally语句。 有关详细信息,请参阅此处。 http://webbugtrack.blogspot.com/2007/11/bug-184-catch-to-try-catch-finally-in.html 这个让我拉头发好几天!男人我恨IE中的调试! **** – 2010-05-31 02:16:51

0

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

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

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

+0

onclick的删除有帮助,但它仍然有重大泄漏,否则。不,全局范围内没有任何内容,它是调用处理Ajax响应的函数的一部分,并且一旦完成,所有内容都会超出范围。 – Tom 2010-05-31 21:32:56

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

因此该解决方案可为提供词汇范围简单,秀n以上