Js記憶體洩露問題總結

weixin_34321977發表於2016-05-26

最近接受了一個Js職位的面試,問了很多Js的高階特性,才發現長時間使用已知的特性進行開發而忽略了對這門語言循序漸進的理解,包括Java我想也是一樣,偶爾在Sun官方看到JDK6.0列舉出來的new features才發現很多東西是自己並不知道或者遺忘了的。看來還是要堅持總結技術,反覆理解和運用才能保持對任何技術的掌握運用能力阿。

翻了一些Js的新老資料,準備先講講Js的記憶體洩露問題——
當一個DOM物件包含一個Js物件的引用(例如一個Event Handler), 而這個Js物件又持有對這個DOM物件的引用時,一個環狀引用就行成了。這本身並不是什麼錯誤或者Bug,因為Js的回收機制能理解這種環狀的引用結構並且在沒有其他物件能關聯到環上的時候回收這個環上的所有物件記憶體。可不幸的是IE瀏覽器中的DOM結構並不受Js解釋機制管理,所以它並不能理解這種失去外界引用的環狀結構,導致環上任何物件都無法被訪問到,可是記憶體依舊佔據著,這也就是所謂的Js記憶體洩露了。

我們來看一個經典的例子說明問題——

JScript code

(function(limit, delay){
var queue=new Array(10);
var n;
function makeSpan(n){
    var s=document.createElement(‘span’);
    document.body.appendChild(s);
    var t=document.createTextNode(‘ ’+n);
    s.appendChild(t);
    s.onclick=function(e){
    s.style.backgroundColor=’red’;
alert(n);
};
return s;
}

function process(n){
    queue.push(makeSpan(n));
    var s=queue.shift();
    if(s)
        s.parentNode.removeChild(s);
}

function loop()}{
    if(n<limit){
    process(n);
    n+=1;
    setTimeout(loop,delay);
}
}
loop();
})(10000,10);

 

 

這個例子的意義是建立出10000個span元素來新增到DOM的body上,並且對其內容填充序號n,緊接著從queue的第一個位置移除建立的span元素,也就是說10000個為止,不斷的建立再移除,永遠只保留最新建立的那10個。這個例子滿足的條件就是DOM元素帶有Js物件即click事件的Event Handler,而Event Handler裡面又帶有這個DOM元素的引用,於是環狀結構行程。

當我在IE上執行這個Js的時候開啟工作管理員,很明顯的看到此網頁的記憶體從55M左右起很穩定的增長直到結束第10000個span建立完畢停止增長時已經到了167M,而在Firefox上執行此Js得到的資料是從頭到尾記憶體都不會超過70M。這已經說明IE一直都沒有解決這種Js記憶體洩露的問題,即使我用的版本已經是最新的IE8.0。

可以想象在如今Ajax運用越來越多,使用者體驗要求越來越高的情況下,網頁的體積會越來越龐大,可能很頻繁的Js程式設計師需要做的事情就是在某個DOM元素例如Div裡新增很多Html程式碼,用innerHTML賦值進去,然後使用者觸發某事件後又整個替換掉innerHTML,那麼被替換以前的Html程式碼很可能帶有這樣的環狀結構,導致頁面只要不重新整理,記憶體就會一直洩露著越來越嚴重,直到吃光機器記憶體。

所以我們提倡人為的打破這種環狀結構, 即在DOM元素被拋棄之前移除繫結在上面的Js Event Handler,移除的方法就涉及到DOM事件模型的討論範圍了,我已在另外一篇討論文章中詳細機講解W3C標準的DOM事件模型和IE的到底有什麼區別了,有興趣的可以看一看。

當然瀏覽器廠商特別是IE也希望能負起一定的責任起來,早日大一統,算是為廣大Js程式設計師造福吧!

相關文章