這篇文章主要介紹JavaScript內(nèi)存泄漏的主要原因,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
創(chuàng)新互聯(lián)建站專注于企業(yè)全網(wǎng)營(yíng)銷推廣、網(wǎng)站重做改版、巨鹿網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5、商城開(kāi)發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為巨鹿等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。
在閱讀這篇博客之前,你或許需要具備一些JavaScript內(nèi)存管理的知識(shí):
內(nèi)存泄露(Memory Leaks):是指應(yīng)用程序已經(jīng)不再需要的內(nèi)存,由于某種原因未返回給操作系統(tǒng)或者空閑內(nèi)存池(Pool of Free Memory)。
內(nèi)存泄露可能帶來(lái)的問(wèn)題:變慢、卡頓、高延遲。
JavaScript內(nèi)存泄漏的主要原因在于一些不再需要的引用(Unwanted References)。
所謂的Unwanted References指的是:有一些內(nèi)存,其實(shí)開(kāi)發(fā)人員已經(jīng)不再需要了,但是由于某種原因,這些內(nèi)存仍然被標(biāo)記并保留在活動(dòng)根目錄樹(shù)中。Unwanted References就是指對(duì)這些內(nèi)存的引用。在JavaScript上下文中,Unwanted References是一些不再使用的變量,這些變量指向了原本可以釋放的一些內(nèi)存。
首先,我們得知道,JavaScript中的全局變量是由根節(jié)點(diǎn)(root node)引用的,因此它們?cè)趹?yīng)用程序的整個(gè)生命周期中都不會(huì)被垃圾回收。
場(chǎng)景一:在JavaScript中,如果引用未聲明的變量,將會(huì)導(dǎo)致,在全局環(huán)境中創(chuàng)建新的變量。
function foo(arg) { bar = "this is a hidden global variable"; }
上面這串代碼,實(shí)際上如下:
function foo(arg) { window.bar = "this is an explicit global variable"; }
假如,我們希望bar這個(gè)變量?jī)H在foo函數(shù)作用域內(nèi)部使用,但上面這種情況就會(huì)意外地在全局作用域內(nèi)創(chuàng)建bar,這將造成內(nèi)存泄漏。
場(chǎng)景二:
function foo() { this.variable = "potential accidental global"; }foo();
同樣的,如果我們希望bar這個(gè)變量?jī)H在foo函數(shù)作用域內(nèi)部使用,但如果不知道foo函數(shù)內(nèi)部的this指向全局對(duì)象,將造成內(nèi)存泄露。
建議:
避免意外地創(chuàng)建全局變量。比如,我們可以使用嚴(yán)格模式,則本節(jié)的第一段代碼將報(bào)錯(cuò),而不會(huì)創(chuàng)建全局變量。
減少創(chuàng)建全局變量。
如果必須使用全局變量來(lái)存儲(chǔ)大量數(shù)據(jù),請(qǐng)確保在處理完數(shù)據(jù)后將其置null或重新分配。
場(chǎng)景舉例:
for (var i = 0; i < 100000; i++) { var buggyObject = { callAgain: function () { var ref = this; var val = setTimeout(function () { ref.callAgain(); }, 10); } } buggyObject.callAgain(); buggyObject = null;}
多處引用(Multiple references):當(dāng)多個(gè)對(duì)象均引用同一對(duì)象時(shí),但凡其中一個(gè)引用沒(méi)有清除,都將導(dǎo)致被引用對(duì)象無(wú)法GC。
場(chǎng)景一:
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); // Much more logic}function removeButton() { // The button is a direct child of body. document.body.removeChild(document.getElementById('button')); // At this point, we still have a reference to #button in the global // elements dictionary. In other words, the button element is still in // memory and cannot be collected by the GC.s}
在上面這種情況中,我們對(duì)#button的保持兩個(gè)引用:一個(gè)在DOM樹(shù)中,另一個(gè)在elements對(duì)象中。 如果將來(lái)決定回收#button,則需要使兩個(gè)引用均不可訪問(wèn)。在上面的代碼中,由于我們只清除了來(lái)自DOM樹(shù)的引用,所以#button仍然存在內(nèi)存中,而不會(huì)被GC。
場(chǎng)景二: 如果我們想要回收某個(gè)table,但我們保持著對(duì)這個(gè)table中某個(gè)單元格(cell)的引用,這個(gè)時(shí)候?qū)?dǎo)致整個(gè)table都保存在內(nèi)存中,無(wú)法GC。
閉包(Closure):閉包是一個(gè)函數(shù),它可以訪問(wèn)那些定義在它的包圍作用域(Enclosing Scope)里的變量,即使這個(gè)包圍作用域已經(jīng)結(jié)束。因此,閉包具有記憶周圍環(huán)境(Context)的功能。
場(chǎng)景舉例:
var newElem;function outer() { var someText = new Array(1000000); var elem = newElem; function inner() { if (elem) return someText; } return function () {}; }setInterval(function () { newElem = outer();}, 5);
在這個(gè)例子中,有兩個(gè)閉包:一個(gè)是inner,另一個(gè)是匿名函數(shù)function () {}
。其中,inner閉包引用了someText和elem,并且,inner永遠(yuǎn)也不會(huì)被調(diào)用??墒?,我們需要注意:相同父作用域的閉包,他們能夠共享context。也就是說(shuō),在這個(gè)例子中,inner的someText和elem將和匿名函數(shù)function () {}
共享。然而,這個(gè)匿名函數(shù)之后會(huì)被return返回,并且賦值給newElem。只要newElem還引用著這個(gè)匿名函數(shù),那么,someText和elem就不會(huì)被GC。
同時(shí),我們還要注意到,outer函數(shù)內(nèi)部執(zhí)行了var elem = newElem;
,而這個(gè)newElem引用了上一次調(diào)用的outer返回的匿名函數(shù)。試想,第n次調(diào)用outer將保持著第n-1次調(diào)用的outer中的匿名函數(shù),而這個(gè)匿名函數(shù)由保持著對(duì)elem的引用,進(jìn)而保持著對(duì)n-2次的...因此,這將造成內(nèi)存泄漏。
解決方案:setInterval中的參數(shù)1的代碼改為newElem = outer()();
這一節(jié)內(nèi)容的具體剖析,可以見(jiàn)資料1和資料2。
Chrome(最新的86版本)開(kāi)發(fā)者工具中有兩個(gè)關(guān)于內(nèi)存的分析工具:
Performance
Memory
以上是JavaScript內(nèi)存泄漏的主要原因的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!