【问题标题】:Javascript memory leak in simple code简单代码中的 Javascript 内存泄漏
【发布时间】:2011-07-01 21:56:54
【问题描述】:

我的 javascript 代码中存在某种内存问题。我一直在网上搜索 javascript 中的内存泄漏模式,但这些模式似乎都不适用于我的代码。这是非常简单的代码,没有闭包,我什至没有内部函数,我所有的变量都只是保存简单的值,比如 DOM 对象或布尔值,我什至没有在我的变量中定义属性。但不知何故,我设法泄漏了如此多的内存,以至于 Firefox (3.6.11) 冻结,IE 告诉我我在第 73 行内存不足,我已经标记了那行。 DOM 树中引用的函数是 toggle、replyForm 和 replyExpand。

<script type="text/javascript">
function toggle(expandable){
    if(expandable.parentNode.getElementsByTagName("ul")[0].className=="hide"){
        expandable.parentNode.getElementsByTagName("ul")[0].className="show";
        expandable.innerHTML="▼";
    }
    else{
        expandable.parentNode.getElementsByTagName("ul")[0].className="hide";
        expandable.innerHTML="►";
    }
}

var previousSpan;

function replyForm(span,postId,op,commentID){
    if(removeForm(span))
        return;
    previousSpan=span;

    if(span.nextSibling!=null){
        span.parentNode.insertBefore(form(span,postId,op,commentID),span.nextSibling);
    }
    else{
        span.parentNode.appendChild(form(span,postId,op,commentID));
    }
}

function removeForm(span){
    if(previousSpan==null)
        return false;  <-- Out of memory here according to IE
    if(previousSpan.parentNode.getElementsByTagName("form").length!=0)
previousSpan.parentNode.removeChild(span.parentNode.getElementsByTagName("form")[0]);
    var result=(span==previousSpan);
    if(result)
        collapse(previousSpan);
previousSpan=null;
return result;
}

function form(span,postId,op,commentID){
var result=
"<form id=\"commentform\" method=\"post\" action=\"http://innategamer.300mb.us/wp-comments-post.php\">\n"+
"<p>Replying to "+op+":</p>\n"+
"<p><label for=\"author\">Name *</label><input id=\"author\" type=\"text\" aria-required=\"true\" size=\"30\" value=\"\" name=\"author\"></p>\n"+
"<p>Email *<input id=\"email\" type=\"text\" aria-required=\"true\" size=\"30\" value=\"\" name=\"email\"></p>\n"+
"<p>Website<input id=\"url\" type=\"text\" size=\"30\" value=\"\" name=\"url\"></p>\n"+
"<textarea id=\"comment\" aria-required=\"true\" rows=\"8\" cols=\"45\" name=\"comment\"></textarea>\n"+
"<input id=\"submit\" type=\"submit\" value=\"Post Comment\" name=\"submit\">\n"+
"<input id=\"comment_post_ID\" type=\"hidden\" value=\""+postId+"\" name=\"comment_post_ID\">\n"+
"<input id=\"comment_parent\" type=\"hidden\" value=\""+commentID+"\" name=\"comment_parent\">\n"+
"</form>";
    var div=document.createElement('div');
    div.innerHTML=result;
    return div.firstChild;
}

function replyExpand(span){
    if(span.innerHTML.indexOf("▼")!=-1)
        collapse(span);
    else
        expand(span);
}

function collapse(span){
    if(previousSpan==span)
        removeForm(span);
    span.innerHTML=span.innerHTML.replace("▼","►");
    while(span.nextSibling!=null){
        span=span.nextSibling;
        if(span.className=="comment-content show");
            span.className="comment-content hide";
    }
}

function expand(span){
    span.innerHTML=span.innerHTML.replace("►","▼");
    while(span.nextSibling!=null){
        span=span.nextSibling;
        if(span.className=="comment-content hide");
            span.className="comment-content show";
    }
}


</script>

【问题讨论】:

    标签: javascript memory-leaks


    【解决方案1】:

    我想这与这两个函数相互调用有关,据我所知,if 语句一旦为真,将永远为真。

    function removeForm(span) {
        if (previousSpan == null) return false; < --Out of memory here according to IE
        if (previousSpan.parentNode.getElementsByTagName("form").length != 0)
            previousSpan.parentNode.removeChild(span.parentNode.getElementsByTagName("form")[0]);
        var result = (span == previousSpan);
        if (result) collapse(previousSpan); // <---------------
        previousSpan = null;  //                               |
        return result;        //                               |
    }                         //                               |
                              //                               |
    function collapse(span) { //                               |
        if (previousSpan == span) removeForm(span); // <--------
        span.innerHTML = span.innerHTML.replace("▼", "►");
        while (span.nextSibling != null) {
            span = span.nextSibling;
            if (span.className == "comment-content show");
            span.className = "comment-content hide";
        }
    }
    

    有效地:

    var previousSpan;
    
    function removeForm( span ) {
        // ...
        if( span == previousSpan ) collapse(previousSpan);
        // ...
    }
    
    function collapse( span ) {  // <---here span is previousSpan,
           // <-- always true because you're doing if(previousSpan == previousSpan)
        if (previousSpan == span) removeForm(span);   
        // ...
    }
    
    // This makes subsequent comparisons in "removeForm()" always true as well,
    //    because now it is guaranteed that "span" is the same as "previousSpan"
    

    所以一旦span == previousSpan 进入removeForm(),你就将previousSpan 传递给collapse()collapse() 做的第一件事是检查它收到的内容是否等于 previousSpan,因为这是首先发送它的测试。

    【讨论】:

    • 是的,你又是对的。而且我现在真的觉得自己很傻。感谢您的帮助,我非常感谢。昨天我看了这个代码几个小时,没有看到。
    • 嗯,我得到的所有答案都是正确的,我选择了这个,因为它包含一些,嗯,“图形”并说明了问题。再次感谢。
    【解决方案2】:

    看起来你那里有一个无限循环。

    removeForm 呼叫collapse

    collapse 呼叫removeForm

    【讨论】:

    • 哦,妈的,我想你是对的!我现在感觉多么愚蠢。谢谢我很高兴你帮助了我!
    【解决方案3】:

    我不打算花时间用细齿梳子来解决这个问题。但我确实看到removeForm 调用collapse,有时又调用removeForm。我首先怀疑的是这两个函数以某种方式以无限递归模式相互调用。

    请尝试在其中一个函数的开头放置断点。

    在您的调试器中,每次遇到此断点时检查调用堆栈的大小。如果调用堆栈扩展直到程序内存不足,那么恭喜,您刚刚在 StackOverflow 上发布了一个真正的 Stack Overflow!

    【讨论】:

    • 你是对的,虽然我昨天实际上看了这个代码几个小时,但我没有发现。现在要选择正确的答案,所有的答案都是正确的,所以我将选择其中包含最多文本的答案。
    • 您可以通过点击帖子左侧数字旁边的“向上”箭头为有用的帖子投票。
    猜你喜欢
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    • 1970-01-01
    • 2012-04-03
    相关资源
    最近更新 更多