JavaScript內(nèi)存管理

作為一門高級語言,JS并不像低級語言C/C++那樣擁有對內(nèi)存的完全掌控。JS中內(nèi)存的分配和回收都是自動完成的,內(nèi)存在不使用的時候會被垃圾回收器自動回收。

正因?yàn)槔厥掌鞯拇嬖?,許多人認(rèn)為JS不用太關(guān)心內(nèi)存管理的問題,但如果不了解JS的內(nèi)存管理機(jī)制,我們同樣非常容易成內(nèi)存泄漏(內(nèi)存無法被回收)的情況。

內(nèi)存的生命周期

JS環(huán)境中分配的內(nèi)存一般有如下生命周期:

  1. 內(nèi)存分配:當(dāng)我們申明變量、函數(shù)、對象的時候,系統(tǒng)會自動為他們分配內(nèi)存
  2. 內(nèi)存使用:即讀寫內(nèi)存,也就是使用變量、函數(shù)等
  3. 內(nèi)存回收:使用完畢,由垃圾回收自動回收不再使用的內(nèi)存

內(nèi)存分配的幾個例子:


// 為變量分配內(nèi)存
var i = 11;
var s = "ifcode";

// 為對象分配內(nèi)存
var person = {
    age: 22,
    name: 'ifcode'
};

// 為函數(shù)分配內(nèi)存
function sum(a, b) {
    return a + b;
}

垃圾回收算法

對垃圾回收算法來說,核心思想就是如何判斷內(nèi)存已經(jīng)不再使用了。下面介紹兩種常見瀏覽器的垃圾回收算法。

引用計(jì)數(shù)算法

熟悉C語言的同學(xué)的都明白,引用無非就是指向某一物體的指針。對不熟悉底層語言的同學(xué)來說,可簡單將引用視為一個對象訪問另一個對象的路徑。(這里的對象是一個寬泛的概念,泛指JS環(huán)境中的實(shí)體)。

引用計(jì)數(shù)算法定義“內(nèi)存不再使用”的標(biāo)準(zhǔn)很簡單,就是看一個對象是否有指向它的引用。如果沒有其他對象指向它了,說明該對象已經(jīng)不再需了。

下面來看個例子:

// 創(chuàng)建一個對象person,他有兩個指向?qū)傩詀ge和name的引用
var person = {
    age: 22,
    name: 'ifcode'
};

person.name = null; // 雖然設(shè)置為null,但因?yàn)閜erson對象還有指向name的引用,因此name不會回收

var p = person; 
person = 1;         //原來的person對象被賦值為1,但因?yàn)橛行乱胮指向原person對象,因此它不會被回收

p = null;           //原person對象已經(jīng)沒有引用,很快會被回收

由上面可以看出,引用計(jì)數(shù)算法是個簡單有效的算法。但它卻存在一個致命的問題:循環(huán)引用。如果兩個對象相互引用,盡管他們已不再使用,垃圾回收器不會進(jìn)行回收,導(dǎo)致內(nèi)存泄露。

function cycle() {
    var o1 = {};
    var o2 = {};
    o1.a = o2;
    o2.a = o1; 
    
    return "Cycle reference!"
}

cycle();

上面我們申明了一個cycle方程,其中包含兩個相互引用的對象。在調(diào)用函數(shù)結(jié)束后,對象o1和o2實(shí)際上已離開函數(shù)范圍,因此不再需要了。但根據(jù)引用計(jì)數(shù)的原則,他們之間的相互引用依然存在,因此這部分內(nèi)存不會被回收,內(nèi)存泄露不可避免了。

正是因?yàn)橛羞@個嚴(yán)重的缺點(diǎn),這個算法在現(xiàn)代瀏覽器中已經(jīng)被下面要介紹的標(biāo)記清除算法所取代了。但絕不可認(rèn)為該問題已經(jīng)不再存在了,因?yàn)檫€占有大量市場的IE6、IE7使用的正是這一算法。在需要照顧兼容性的時候,某些看起來非常普通的寫法也可能造成意想不到的問題:

var div = document.createElement("div");
div.onclick = function() {
    console.log("click");
};

上面這種JS寫法再普通不過了,創(chuàng)建一個DOM元素并綁定一個點(diǎn)擊事件。那么這里有什么問題呢?請注意,變量div有事件處理函數(shù)的引用,同時事件處理函數(shù)也有div的引用?。╠iv變量可在函數(shù)內(nèi)被訪問)。一個循序引用出現(xiàn)了,按上面所講的算法,該部分內(nèi)存無可避免地泄露哦了。

現(xiàn)在你明白為啥前端程序員都討厭IE6了吧?擁有超多BUG并依然占有大量市場的IE6是前端開發(fā)一生之?dāng)?!親,沒有買賣就沒有殺害。為了讓你身邊的前端程序員活得健康一些,請今天就升級你的瀏覽器吧!

標(biāo)記清除算法

上面說過,現(xiàn)代的瀏覽器已經(jīng)不再使用引用計(jì)數(shù)算法了。現(xiàn)代瀏覽器通用的大多是基于標(biāo)記清除算法的某些改進(jìn)算法,總體思想都是一致的。

標(biāo)記清除算法將“不再使用的對象”定義為“無法達(dá)到的對象”。簡單來說,就是從根部(在JS中就是全局對象)出發(fā)定時掃描內(nèi)存中的對象。凡是能從根部到達(dá)的對象,都是還需要使用的。那些無法由根部出發(fā)觸及到的對象被標(biāo)記為不再使用,稍后進(jìn)行回收。

從這個概念可以看出,無法觸及的對象包含了沒有引用的對象這個概念(沒有任何引用的對象也是無法觸及的對象)。但反之未必成立。

根據(jù)這個概念,上面的例子可以正確被垃圾回收處理了。當(dāng)div與其時間處理函數(shù)不能再從全局對象出發(fā)觸及的時候,垃圾回收器就會標(biāo)記并回收這兩個對象。

如何寫出對內(nèi)存管理友好的JS代碼?

如果還需要兼容老舊瀏覽器,那么就需要注意代碼中的循環(huán)引用問題?;蛘咧苯硬捎帽WC兼容性的庫來幫助優(yōu)化代碼。

對現(xiàn)代瀏覽器來說,唯一要注意的就是明確切斷需要回收的對象與根部的聯(lián)系。有時候這種聯(lián)系并不明顯,且因?yàn)闃?biāo)記清除算法的強(qiáng)壯性,這個問題較少出現(xiàn)。最常見的內(nèi)存泄露一般都與DOM元素綁定有關(guān):

email.message = document.createElement(“div”);
displayList.appendChild(email.message);

// 稍后從displayList中清除DOM元素
displayList.removeAllChildren();

div元素已經(jīng)從DOM樹中清除,也就是說從DOM樹的根部無法觸及該div元素了。但是請注意,div元素同時也綁定了email對象。所以只要email對象還存在,該div元素將一直保存在內(nèi)存中。

小結(jié)

如果你的引用只包含少量JS交互,那么內(nèi)存管理不會對你造成太多困擾。一旦你開始構(gòu)建中大規(guī)模的SPA或是服務(wù)器和桌面端的應(yīng)用,那么就應(yīng)當(dāng)將內(nèi)存泄露提上日程了。不要滿足于寫出能運(yùn)行的程序,也不要認(rèn)為機(jī)器的升級就能解決一切。當(dāng)你從初級程序員走向資深的時候,關(guān)注細(xì)節(jié)真是你能脫穎而出的優(yōu)點(diǎn)。

參考

Effectively Managing Memory at Gmail scale

Static Memory Javascript with Object Pools

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

相關(guān)閱讀更多精彩內(nèi)容

  • 第一章 簡介 J2SE平臺的一大優(yōu)勢是它的自動化內(nèi)存管理,避免了開發(fā)者去面對內(nèi)存管理的復(fù)雜性。 本文以Sun J2...
    tianyiliusha閱讀 1,072評論 0 1
  • 內(nèi)存聲明周期 分配你所需要的內(nèi)存 使用分配的內(nèi)存(讀寫) 不再需要時釋放內(nèi)存 內(nèi)存分配 javascript在聲明...
    言葉丶閱讀 261評論 0 0
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虛擬機(jī)(JVM)垃圾回收器提供...
    簡欲明心閱讀 90,343評論 17 311
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,755評論 11 349
  • 目錄頁:我的algs4之旅 Union-Find是Algorithms, Part I第一周的第二部分。該部分的編...
    懂時已不是當(dāng)時閱讀 1,419評論 0 0

友情鏈接更多精彩內(nèi)容