JS中4種常見的內存泄漏
一、什麽是內存泄漏
本質上講,內存泄漏是當一塊內存不再被應用程序使用的時候,由於某種原因,這塊內存沒有返還給操作系統或空閑內存池的現象。
二、幾種常見的內存泄漏
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
)的對象。
同時,沒有用到的那個變量持有一個引用了 originalThing
(replaceThing
調用之前的 theThing
)閉包。
關鍵的問題是每當在同一個父作用域下創建閉包作用域的時候,這個作用域是被共享的。在這種情況下,someMethod
的閉包作用域和 unused
的作用域是共享的。
unused
持有一個 originalThing
的引用。盡管 unused
從來沒有被使用過,someMethod
可以在 theThing
之外被訪問。
而且 someMethod
和 unused
共享了閉包作用域,即便 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種常見的內存泄漏