1. 程式人生 > >JS中4種常見的內存泄漏

JS中4種常見的內存泄漏

global long int message fun this指向 由於 回調 pan

一、什麽是內存泄漏

本質上講,內存泄漏是當一塊內存不再被應用程序使用的時候,由於某種原因,這塊內存沒有返還給操作系統或空閑內存池的現象。

二、幾種常見的內存泄漏

1、意外的全局變量

一個未聲明變量的引用會在全局對象中創建一個新的變量。在瀏覽器的環境下,全局對象就是window,也就是說:

function foo(arg) {
    bar = "this is a hidden global variable";
}

實際上是:

function foo(arg) {
    window.bar = "this is an explicit global variable";
}

上面代碼中,如果bar是一個應該指向foo函數作用域內變量的引用,但忘記使用var來聲明這個變量,這時就相當於創建了一個全局變量。

另外一種偶然創建全局變量的方式如下:

function foo() {
    this.variable = "potential accidental global";
} 
foo();

上面代碼中,foo函數再全局作用域中被調用,因此this指向window

全局變量的註意事項:

如果需要全局變量來存儲很多數據,必須確保在使用過後將它設置為null或重新為他賦值。

常見的和全局變量相關的引發內存消耗增長的原因是緩存。緩存存儲著可復用的數據。

為了讓這種做法更高效,必須為緩存的容量規定一個上界。由於緩存不能被及時回收的緣故,緩存無限制地增長會導致很高的內存消耗。

2、閉包引起的內存泄漏

閉包可以使變量常駐內存,但如果使用不當就會在成內存泄漏

var theThing = null;
var replaceThing = function () {
  var originalThing = theThing;
  var unused = function () {
    if (originalThing)
      console.log("hi");
  };
  theThing = {
    longStr: new Array(1000000).join(‘*‘),
    someMethod: function () {
      console.log(someMessage);
    }
  };
};
setInterval(replaceThing, 
1000);

上面代碼中,每次調用 replaceThing 時,theThing 都會得到新的包含一個大數組和新的閉包(someMethod)的對象。

同時,沒有用到的那個變量持有一個引用了 originalThingreplaceThing 調用之前的 theThing)閉包。

關鍵的問題是每當在同一個父作用域下創建閉包作用域的時候,這個作用域是被共享的。在這種情況下,someMethod 的閉包作用域和 unused 的作用域是共享的。

unused 持有一個 originalThing 的引用。盡管 unused 從來沒有被使用過,someMethod 可以在 theThing 之外被訪問。

而且 someMethodunused 共享了閉包作用域,即便 unused 從來都沒有被使用過,它對 originalThing 的引用還是強制它保持活躍狀態(阻止它被回收)。

當這段代碼重復運行時,將可以觀察到內存消耗穩定地上漲,並且不會因為 GC 的存在而下降。

本質上來講,創建了一個閉包鏈表(根節點是 theThing 形式的變量),而且每個閉包作用域都持有一個對大數組的間接引用,這導致了一個巨大的內存泄露。

3、DOM之外的引用

var elements={  
    button: document.getElementById("button"),  
    image: document.getElementById("image"),  
    text: document.getElementById("text")  
};  
function doStuff(){  
    image.src="http://some.url/image";  
    button.click():  
    console.log(text.innerHTML)  
}  
function removeButton(){  
    document.body.removeChild(document.getElementById(‘button‘))  
}  

2、被遺漏的定時器和回調函數

var someResouce=getData();  
setInterval(function(){  
    var node=document.getElementById(‘Node‘);  
    if(node){  
        node.innerHTML=JSON.stringify(someResouce)  
    }  
},1000) 

上面代碼中, 如果 id 為 Node 的元素從 DOM 中移除, 該定時器仍會存在, 同時, 因為回調函數中包含對 someResource 的引用, 定時器外面的 someResource 也不會被釋放。

三、怎樣避免內存泄漏

1)減少不必要的全局變量,或者生命周期較長的對象,及時對無用的數據進行垃圾回收;

2)註意程序邏輯,避免“死循環”之類的 ;

3)避免創建過多的對象 原則:不用了的東西要及時歸還。

JS中4種常見的內存泄漏