記憶體泄露是指一塊被分配的記憶體既不能使用,又不能回收,直到瀏覽器進程結束。在C++中,因為是手動管理記憶體,記憶體泄露是經常出現的事情。而現在流行的C#和Java等語言採用了自動記憶體回收方法管理記憶體,正常使用的情況下幾乎不會發生記憶體泄露。瀏覽器中也是採用自動記憶體回收方法管理記憶體,但由於瀏覽器記憶體回收方法有bug,會產生記憶體泄露。
1、當頁面中元素被移除或替換時,若元素繫結的事件仍沒被移除,在IE中不會作出恰當處理,此時要先手工移除事件,不然會存在記憶體泄露。
<div id="myDiv"> <input type="button" value="Click me" id="myBtn"></div><script type="text/javascript"> var btn = document.getElementById("myBtn"); btn.onclick = function(){ document.getElementById("myDiv").innerHTML = "Processing..."; }</script>
應改成下面
<div id="myDiv"> <input type="button" value="Click me" id="myBtn"></div><script type="text/javascript"> var btn = document.getElementById("myBtn"); btn.onclick = function(){ btn.onclick = null; document.getElementById("myDiv").innerHTML = "Processing..."; }</script>
或者採用事件委託
<div id="myDiv"> <input type="button" value="Click me" id="myBtn"></div><script type="text/javascript"> document.onclick = function(event){ event = event || window.event; if(event.target.id == "myBtn"){ document.getElementById("myDiv").innerHTML = "Processing..."; } }</script>
2、
var a=document.getElementById("#xx");
var b=document.getElementById("#xxx");
a.r=b;
b.r=a;
var a=document.getElementById("#xx");a.r=a;
對於純粹的 ECMAScript 對象而言,只要沒有其他對象引用對象 a、b,也就是說它們只是相互之間的引用,那麼仍然會被垃圾收集系統識別並處理。但是,在 Internet Explorer 中,如果循環參考中的任何對象是 DOM 節點或者 ActiveX 對象,垃圾收集系統則不會發現它們之間的迴圈關係與系統中的其他對象是隔離的並釋放它們。最終它們將被保留在記憶體中,直到瀏覽器關閉。
3、
var elem = document.getElementById('test');elem.addEventListener('click', function() { alert('You clicked ' + elem.tagName);});
這段代碼把一個匿名函數註冊為一個DOM結點的click事件處理函數,函數內引用了一個DOM對象elem,就形成了閉包。這就會產生一個循環參考,即:DOM->閉包->DOM->閉包...DOM對象在閉包釋放之前不會被釋放;而閉包作為DOM對象的事件處理函數存在,所以在DOM對象釋放前閉包不會釋放,即使DOM對象在DOM tree中刪除,由於這個循環參考的存在,DOM對象和閉包都不會被釋放。可以用下面的方法可以避免這種記憶體泄露
var elem = document.getElementById('test');elem.addEventListener('click', function() { alert('You clicked ' + this.tagName); // 不再直接引用elem變數});
4、
function bindEvent() { var obj=document.createElement("XXX"); obj.onclick=function(){ //Even if it's a empty function } }
閉包非常容易構成循環參考。如果一個構成閉包的函數對象被指定給,比如一個 DOM 節點的事件處理器,而對該節點的引用又被指定給函數對象範圍中的一個活動(或可變)對象,那麼就存在一個循環參考。
DOM_Node.onevent -<function_object.[[scope]] -<scope_chain -<Activation_object.nodeRef -<DOM_Node。
形成這樣一個循環參考是輕而易舉,而且稍微瀏覽一下包含類似循環參考代碼的網站(通常會出現在網站的每個頁面中),就會消耗大量(甚至全部)系統記憶體。
解決之道,將事件處理函數定義在外部,解除閉包
function bindEvent() { var obj=document.createElement("XXX"); obj.onclick=onclickHandler; } function onclickHandler(){ //do something }
或者在定義事件處理函數的外部函數中,刪除對dom的引用(題外,《JavaScript權威指南》中介紹過,閉包中,範圍中沒用的屬性可以刪除,以減少記憶體消耗。)
function bindEvent() { var obj=document.createElement("XXX"); obj.onclick=function(){ //Even if it's a empty function } obj=null; }
5、
a = {p: {x: 1}};b = a.p;delete a.p;
執行這段代碼之後b.x的值依然是1.由於已經刪除的屬性引用依然存在,因此在JavaScript的某些實現中,可能因為這種不嚴謹的代碼而造成記憶體泄露。所以在銷毀對象的時候,要遍曆屬性中屬性,依次刪除。
6. 自動類型裝箱轉換
別不相信,下面的代碼在ie系列中會導致記憶體泄露
var s=”lalala”;alert(s.length);
s本身是一個string而非object,它沒有length屬性,所以當訪問length時,JS引擎會自動建立一個臨時String對象封裝s,而這個對象一定會泄露。這個bug匪夷所思,所幸解決起來相當容易,記得所有實值型別做.運算之前先顯式轉換一下:
var s="lalala";alert(new String(s).length);
7、某些DOM操作
IE系列的特有問題 簡單的來說就是在向不在DOM樹上的DOM元素appendChild;IE7中,貌似為了改善記憶體泄露,IE7採用了極端的解決方案:離開頁面時回收所有DOM樹上的元素,其它一概不管。
本文首發: http://www.cnblogs.com/sprying/archive/2013/05/31/3109517.html
以上知識點來源於《JavaScript進階程式設計》和《JavaScript權威指南》和 http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html#clSto
http://www.uml.org.cn/site/201205294.asp
《JavaScript進階程式設計》有關記憶體部分http://bbs.phpchina.com/thread-221214-1-1.html