js內(nèi)存泄露的幾種情況詳細探討
更新時間:2013年05月31日 17:43:52 作者:
內(nèi)存泄露是指一塊被分配的內(nèi)存既不能使用,又不能回收,直到瀏覽器進程結(jié)束,由于瀏覽器垃圾回收方法有bug,會產(chǎn)生內(nèi)存泄露,下面與大家詳細探討下內(nèi)存泄露的幾種情況
內(nèi)存泄露是指一塊被分配的內(nèi)存既不能使用,又不能回收,直到瀏覽器進程結(jié)束。在C++中,因為是手動管理內(nèi)存,內(nèi)存泄露是經(jīng)常出現(xiàn)的事情。而現(xiàn)在流行的C#和Java等語言采用了自動垃圾回收方法管理內(nèi)存,正常使用的情況下幾乎不會發(fā)生內(nèi)存泄露。瀏覽器中也是采用自動垃圾回收方法管理內(nèi)存,但由于瀏覽器垃圾回收方法有bug,會產(chǎn)生內(nèi)存泄露。
1、當頁面中元素被移除或替換時,若元素綁定的事件仍沒被移除,在IE中不會作出恰當處理,此時要先手工移除事件,不然會存在內(nèi)存泄露。
<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>
應(yīng)改成下面
<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,也就是說它們只是相互之間的引用,那么仍然會被垃圾收集系統(tǒng)識別并處理。但是,在 Internet Explorer 中,如果循環(huán)引用中的任何對象是 DOM 節(jié)點或者 ActiveX 對象,垃圾收集系統(tǒng)則不會發(fā)現(xiàn)它們之間的循環(huán)關(guān)系與系統(tǒng)中的其他對象是隔離的并釋放它們。最終它們將被保留在內(nèi)存中,直到瀏覽器關(guān)閉。
3、
var elem = document.getElementById('test');
elem.addEventListener('click', function() {
alert('You clicked ' + elem.tagName);
});
這段代碼把一個匿名函數(shù)注冊為一個DOM結(jié)點的click事件處理函數(shù),函數(shù)內(nèi)引用了一個DOM對象elem,就形成了閉包。這就會產(chǎn)生一個循環(huán)引用,即:DOM->閉包->DOM->閉包...DOM對象在閉包釋放之前不會被釋放;而閉包作為DOM對象的事件處理函數(shù)存在,所以在DOM對象釋放前閉包不會釋放,即使DOM對象在DOM tree中刪除,由于這個循環(huán)引用的存在,DOM對象和閉包都不會被釋放??梢杂孟旅娴姆椒梢员苊膺@種內(nèi)存泄露
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
}
}
閉包非常容易構(gòu)成循環(huán)引用。如果一個構(gòu)成閉包的函數(shù)對象被指定給,比如一個 DOM 節(jié)點的事件處理器,而對該節(jié)點的引用又被指定給函數(shù)對象作用域中的一個活動(或可變)對象,那么就存在一個循環(huán)引用。
DOM_Node.onevent -<function_object.[[scope]] -<scope_chain -<Activation_object.nodeRef -<DOM_Node。
形成這樣一個循環(huán)引用是輕而易舉的,而且稍微瀏覽一下包含類似循環(huán)引用代碼的網(wǎng)站(通常會出現(xiàn)在網(wǎng)站的每個頁面中),就會消耗大量(甚至全部)系統(tǒng)內(nèi)存。
解決之道,將事件處理函數(shù)定義在外部,解除閉包
function bindEvent()
{
var obj=document.createElement("XXX");
obj.onclick=onclickHandler;
}
function onclickHandler(){
//do something
}
或者在定義事件處理函數(shù)的外部函數(shù)中,刪除對dom的引用(題外,《JavaScript權(quán)威指南》中介紹過,閉包中,作用域中沒用的屬性可以刪除,以減少內(nèi)存消耗。)
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;
執(zhí)行這段代碼之后b.x的值依然是1.由于已經(jīng)刪除的屬性引用依然存在,因此在JavaScript的某些實現(xiàn)中,可能因為這種不嚴謹?shù)拇a而造成內(nèi)存泄露。所以在銷毀對象的時候,要遍歷屬性中屬性,依次刪除。
6. 自動類型裝箱轉(zhuǎn)換
別不相信,下面的代碼在ie系列中會導(dǎo)致內(nèi)存泄露
var s=”lalala”;
alert(s.length);
s本身是一個string而非object,它沒有l(wèi)ength屬性,所以當訪問length時,JS引擎會自動創(chuàng)建一個臨時String對象封裝s,而這個對象一定會泄露。這個bug匪夷所思,所幸解決起來相當容易,記得所有值類型做.運算之前先顯式轉(zhuǎn)換一下:
var s="lalala";
alert(new String(s).length);
7、某些DOM操作
IE系列的特有問題 簡單的來說就是在向不在DOM樹上的DOM元素appendChild;IE7中,貌似為了改善內(nèi)存泄露,IE7采用了極端的解決方案:離開頁面時回收所有DOM樹上的元素,其它一概不管。
1、當頁面中元素被移除或替換時,若元素綁定的事件仍沒被移除,在IE中不會作出恰當處理,此時要先手工移除事件,不然會存在內(nèi)存泄露。
復(fù)制代碼 代碼如下:
<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>
應(yīng)改成下面
復(fù)制代碼 代碼如下:
<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>
或者采用事件委托
復(fù)制代碼 代碼如下:
<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、
復(fù)制代碼 代碼如下:
var a=document.getElementById("#xx");
var b=document.getElementById("#xxx");
a.r=b;
b.r=a;
復(fù)制代碼 代碼如下:
var a=document.getElementById("#xx");
a.r=a;
對于純粹的 ECMAScript 對象而言,只要沒有其他對象引用對象 a、b,也就是說它們只是相互之間的引用,那么仍然會被垃圾收集系統(tǒng)識別并處理。但是,在 Internet Explorer 中,如果循環(huán)引用中的任何對象是 DOM 節(jié)點或者 ActiveX 對象,垃圾收集系統(tǒng)則不會發(fā)現(xiàn)它們之間的循環(huán)關(guān)系與系統(tǒng)中的其他對象是隔離的并釋放它們。最終它們將被保留在內(nèi)存中,直到瀏覽器關(guān)閉。
3、
復(fù)制代碼 代碼如下:
var elem = document.getElementById('test');
elem.addEventListener('click', function() {
alert('You clicked ' + elem.tagName);
});
這段代碼把一個匿名函數(shù)注冊為一個DOM結(jié)點的click事件處理函數(shù),函數(shù)內(nèi)引用了一個DOM對象elem,就形成了閉包。這就會產(chǎn)生一個循環(huán)引用,即:DOM->閉包->DOM->閉包...DOM對象在閉包釋放之前不會被釋放;而閉包作為DOM對象的事件處理函數(shù)存在,所以在DOM對象釋放前閉包不會釋放,即使DOM對象在DOM tree中刪除,由于這個循環(huán)引用的存在,DOM對象和閉包都不會被釋放??梢杂孟旅娴姆椒梢员苊膺@種內(nèi)存泄露
復(fù)制代碼 代碼如下:
var elem = document.getElementById('test');
elem.addEventListener('click', function() {
alert('You clicked ' + this.tagName); // 不再直接引用elem變量
});
4、
復(fù)制代碼 代碼如下:
function bindEvent()
{
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it's a empty function
}
}
閉包非常容易構(gòu)成循環(huán)引用。如果一個構(gòu)成閉包的函數(shù)對象被指定給,比如一個 DOM 節(jié)點的事件處理器,而對該節(jié)點的引用又被指定給函數(shù)對象作用域中的一個活動(或可變)對象,那么就存在一個循環(huán)引用。
DOM_Node.onevent -<function_object.[[scope]] -<scope_chain -<Activation_object.nodeRef -<DOM_Node。
形成這樣一個循環(huán)引用是輕而易舉的,而且稍微瀏覽一下包含類似循環(huán)引用代碼的網(wǎng)站(通常會出現(xiàn)在網(wǎng)站的每個頁面中),就會消耗大量(甚至全部)系統(tǒng)內(nèi)存。
解決之道,將事件處理函數(shù)定義在外部,解除閉包
復(fù)制代碼 代碼如下:
function bindEvent()
{
var obj=document.createElement("XXX");
obj.onclick=onclickHandler;
}
function onclickHandler(){
//do something
}
或者在定義事件處理函數(shù)的外部函數(shù)中,刪除對dom的引用(題外,《JavaScript權(quán)威指南》中介紹過,閉包中,作用域中沒用的屬性可以刪除,以減少內(nèi)存消耗。)
復(fù)制代碼 代碼如下:
function bindEvent()
{
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it's a empty function
}
obj=null;
}
5、
復(fù)制代碼 代碼如下:
a = {p: {x: 1}};
b = a.p;
delete a.p;
執(zhí)行這段代碼之后b.x的值依然是1.由于已經(jīng)刪除的屬性引用依然存在,因此在JavaScript的某些實現(xiàn)中,可能因為這種不嚴謹?shù)拇a而造成內(nèi)存泄露。所以在銷毀對象的時候,要遍歷屬性中屬性,依次刪除。
6. 自動類型裝箱轉(zhuǎn)換
別不相信,下面的代碼在ie系列中會導(dǎo)致內(nèi)存泄露
復(fù)制代碼 代碼如下:
var s=”lalala”;
alert(s.length);
s本身是一個string而非object,它沒有l(wèi)ength屬性,所以當訪問length時,JS引擎會自動創(chuàng)建一個臨時String對象封裝s,而這個對象一定會泄露。這個bug匪夷所思,所幸解決起來相當容易,記得所有值類型做.運算之前先顯式轉(zhuǎn)換一下:
復(fù)制代碼 代碼如下:
var s="lalala";
alert(new String(s).length);
7、某些DOM操作
IE系列的特有問題 簡單的來說就是在向不在DOM樹上的DOM元素appendChild;IE7中,貌似為了改善內(nèi)存泄露,IE7采用了極端的解決方案:離開頁面時回收所有DOM樹上的元素,其它一概不管。
您可能感興趣的文章:
- 跟我學(xué)習(xí)javascript的垃圾回收機制與內(nèi)存管理
- js 內(nèi)存釋放問題
- JavaScript也談內(nèi)存優(yōu)化
- 解決js函數(shù)閉包內(nèi)存泄露問題的辦法
- 理解Javascript_01_理解內(nèi)存分配原理分析
- IE JS編程需注意的內(nèi)存釋放問題
- 容易造成JavaScript內(nèi)存泄露幾個方面
- js變量、作用域及內(nèi)存詳解
- Javascript 閉包引起的IE內(nèi)存泄露分析
- 深入理解JavaScript程序中內(nèi)存泄漏
- 詳解Nodejs內(nèi)存治理
- javascript 內(nèi)存模型實例詳解
相關(guān)文章
js設(shè)置function參數(shù)默認值(適合沒有傳參情況)
div+css模擬js信息框的類庫時遇到一個問題當沒有傳遞參數(shù)過去時自動使用提示信息作為窗口標題,具體的實現(xiàn)如下2014-02-02JavaScript將一個數(shù)組插入到另一個數(shù)組的方法
這篇文章主要介紹了JavaScript將一個數(shù)組插入到另一個數(shù)組的方法,涉及javascript中Array.prototype.push.apply方法的使用技巧,非常具有實用價值,需要的朋友可以參考下2015-03-03微信jssdk踩坑之簽名錯誤invalid signature
這篇文章主要介紹了微信jssdk踩坑之簽名錯誤invalid signature,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05js 去掉空格實例 Trim() LTrim() RTrim()
js 去掉空格實例Trim(),LTrim(),RTrim() 需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01