js之內存泄漏

一、JavaScript 的垃圾收集機制

javascript具有自動垃圾收集機制,也就是說,執(zhí)行環(huán)境會負責管理代碼執(zhí)行過程中的使用的內存。而在C和C++之類的語言中,開發(fā)人員的一項基本任務就是手動跟蹤內存的使用情況,這是造成許多問題的一個根源。在編寫javascript程序時候,開發(fā)人員不用再關心內存使用的問題,所需內存的分配 以及無用的回收完全實現(xiàn)了自動管理。

JavaScript中最常用的垃圾收集方式是標記清除(mark-and-sweep)。當變量進入環(huán)境(例如,在函數(shù)中聲明一個變量)時,就將這個變量標記為“進入環(huán)境”。從邏輯上講,永遠不能釋放進入環(huán)境的變量所占的內存,因為只要執(zhí)行流進入相應的環(huán)境,就可能用到它們。而當變量離開環(huán)境時,這將其 標記為“離開環(huán)境”。

二、什么是內存泄漏

內存泄露是指一塊被分配的內存既不能使用,又不能回收,直到瀏覽器進程結束。在C++中,因為是手動管理內存,內存泄露是經常出現(xiàn)的事情。而現(xiàn)在流行的C#和Java等語言采用了自動垃圾回收方法管理內存,正常使用的情況下幾乎不會發(fā)生內存泄露。瀏覽器中也是采用自動垃圾回收方法管理內存,但由于瀏覽器垃圾回收方法有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、對于純粹的 ECMAScript 對象而言,只要沒有其他對象引用對象 a、b,也就是說它們只是相互之間的引用,那么仍然會被垃圾收集系統(tǒng)識別并處理。但是,在 Internet Explorer 中,如果循環(huán)引用中的任何對象是 DOM 節(jié)點或者 ActiveX 對象,垃圾收集系統(tǒng)則不會發(fā)現(xiàn)它們之間的循環(huán)關系與系統(tǒng)中的其他對象是隔離的并釋放它們。最終它們將被保留在內存中,直到瀏覽器關閉。

var a=document.getElementById("xx");
var b=document.getElementById("xxx");
a.r=b;
b.r=a;
var a=document.getElementById("xx");
a.r=a;

3

function bindEvent() 
{ 
    var obj=document.createElement("XXX");   // 被閉包所引用,不會被回收
    obj.onclick=function(){    
        //Even if it's a empty function 
    } 
}

閉包可以維持函數(shù)內局部變量,使其得不到釋放。
上例定義事件回調時,由于是函數(shù)內定義函數(shù),并且內部函數(shù)--事件回調的引用外暴了,形成了閉包
解決之道,將事件處理函數(shù)定義在外部,解除閉包

function bindEvent() 
{ 
    var obj=document.createElement("XXX"); 
    obj.onclick=onclickHandler; 
} 
function onclickHandler(){ 
    //do something 
}

或者在定義事件處理函數(shù)的外部函數(shù)中,刪除對dom的引用(題外,《JavaScript權威指南》中介紹過,閉包中,作用域中沒用的屬性可以刪除,以減少內存消耗。)

function bindEvent() 
{ 
    var obj=document.createElement("XXX"); 
    obj.onclick=function(){ 
        //Even if it's a empty function 
    } 
    obj=null; 
}

4、被遺忘的計時器或回調函數(shù)

var someResource = getData(); 
setInterval(function() { 
    var node = document.getElementById('Node'); 
    if(node) { 
        // 處理 node 和 someResource 
        node.innerHTML = JSON.stringify(someResource)); 
    } 
}, 1000);

此例說明了什么:與節(jié)點或數(shù)據關聯(lián)的計時器不再需要,node 對象可以刪除,整個回調函數(shù)也不需要了。可是,計時器回調函數(shù)仍然沒被回收(計時器停止才會被回收)。同時,someResource 如果存儲了大量的數(shù)據,也是無法被回收的。

對于觀察者的例子,一旦它們不再需要(或者關聯(lián)的對象變成不可達),明確地移除它們非常重要。老的 IE 6 是無法處理循環(huán)引用的。如今,即使沒有明確移除它們,一旦觀察者對象變成不可達,大部分瀏覽器是可以回收觀察者處理函數(shù)的。

5、

a = {p: {x: 1}};
b = a.p;
delete a.p;

執(zhí)行這段代碼之后b.x的值依然是1.由于已經刪除的屬性引用依然存在,因此在JavaScript的某些實現(xiàn)中,可能因為這種不嚴謹?shù)拇a而造成內存泄露。所以在銷毀對象的時候,要遍歷屬性中屬性,依次刪除。

6、 自動類型裝箱轉換
看網上資料,說下面的代碼在ie系列中會導致內存泄露,先提個神,具體泄露與否先不管

var s=”lalala”;
alert(s.length);

s本身是一個string而非object,它沒有l(wèi)ength屬性,所以當訪問length時,JS引擎會自動創(chuàng)建一個臨時String對象封裝s,而這個對象一定會泄露。這個bug匪夷所思,所幸解決起來相當容易,記得所有值類型做.運算之前先顯式轉換一下:

var s="lalala";
alert(new String(s).length);

7、某些DOM操作
IE系列的特有問題 簡單的來說就是在向不在DOM樹上的DOM元素appendChild;IE7中,貌似為了改善內存泄露,IE7采用了極端的解決方案:離開頁面時回收所有DOM樹上的元素,其它一概不管。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容