代码之家  ›  专栏  ›  技术社区  ›  Tom

javascript内存泄漏/性能问题?

  •  2
  • Tom  · 技术社区  · 14 年前

    我一辈子都无法在Internet Explorer中发现内存泄漏。

    insertTags 简单Takes字符串 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次(surah 2中的诗句,古兰经中最长的surah)。

    *IE7和IE8内存泄漏,不确定是否为6,但在Safari 4、FF 3.6、Opera 10.5、Chrome 5中运行良好…*

    4 回复  |  直到 11 年前
        1
  •  6
  •   Jeff Meatball Yang    14 年前

    变量的作用域是函数,而不是if/else/for/while/etc.块。每次你打电话

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

    您正在创建一个新函数(新范围)。此新函数引用 i response 变量。所以现在, 响应 同时作用于外部函数和新函数。

    这不足以泄露内存。( 响应 是新函数完成后超出范围的正常变量)

    但是,你创造了一个 p 新函数中的dom元素,并在外部函数中引用它(返回到 fragment.appendChild 作为参数调用)。现在想想:你有外窥镜 fragment 引用一个 从内部作用域创建的DOM,需要使用 响应 变量从外部作用域开始创建DOM元素。

    这个 片段 DOM对象彼此都有一个引用。尽管您试图通过使变量指针无效来消除引用计数, p=null fragment = null 不会删除所有引用。这个 片段 仍然有一个内部的参考 ,它仍然引用外部 响应 变量。这两个“作用域”将永远不会被垃圾收集,因为这仍然是循环依赖关系。

    任何人,如果我犯了什么错误,请纠正我。


    对于解决方案,不要使用内部函数!

    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
  •  2
  •   Premshankar Tiwari    11 年前

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

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

    因此,解决方案可以简单到提供 词法范围 ,如上所示

        3
  •  0
  •   wombleton    14 年前

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

    您可以尝试删除重复的onclick并用事件委托替换它。

    而且,所有的变量似乎都在全局范围内——这不应该像导致您所看到的问题那样糟糕,但是无论如何,您都应该解决这个问题。

        4
  •  -2
  •   deceze    14 年前

    我真的不能告诉你 为什么? 据说是内存泄漏,但这段代码的功能相当复杂。这条线似乎非常可疑和多余: 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引擎上可能需要一段时间。