JS垃圾回收機(jī)制

瀏覽器的垃圾回收機(jī)制(Garbage collection
),簡(jiǎn)稱(chēng)GC烦味,它會(huì)周期性運(yùn)行以釋放那些不需要的內(nèi)存聂使,否則,JavaScript的解釋器將會(huì)耗盡全部系統(tǒng)內(nèi)存而導(dǎo)致系統(tǒng)崩潰谬俄。

具體到瀏覽器中的實(shí)現(xiàn)柏靶,通常有兩個(gè)策略:標(biāo)記清除和引用計(jì)數(shù)。

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

此算法把“對(duì)象是否不再需要”簡(jiǎn)化定義為“對(duì)象有沒(méi)有其他對(duì)象引用到它”溃论。如果沒(méi)有引用指向該對(duì)象(零引用)屎蜓,對(duì)象將被垃圾回收機(jī)制回收。

let car = {
    logo: 'luhu',
    price: 100
}   // car 是第一個(gè)對(duì)這個(gè)對(duì)象的引用

let jeep = car // jeep是第二個(gè)對(duì)這個(gè)對(duì)象的引用
car.logo = null // logo屬性雖然設(shè)置為null 但此屬性還在被jeep引用 不會(huì)被回收
car = '' //  此時(shí)這個(gè)對(duì)象還有有jeep一個(gè)引用
jeep = null // jeep設(shè)置為null 那個(gè)對(duì)象是零引用了 可以被回收了

引用計(jì)數(shù)法是最初級(jí)的垃圾收集算法钥勋,如果某對(duì)象沒(méi)有其他對(duì)象指向它了炬转,那就說(shuō)明它可以被回收辆苔。但是它無(wú)法處理循環(huán)引用的問(wèn)題。

循環(huán)引用的限制

// 引用計(jì)數(shù)法的限制
function f(){
  let o1 = {}
  let o2 = {}
  o1.a = o2
  o2.a = o1

  return 100
}

f()

我們執(zhí)行f函數(shù)扼劈,它返回了一個(gè)數(shù)字驻啤,和內(nèi)部的o1,o2沒(méi)什么關(guān)系,但是對(duì)引用計(jì)數(shù)法來(lái)說(shuō)测僵,o1,o2它們之間還存在著相互引用街佑,并不會(huì)被回收。這就造成了內(nèi)存泄漏捍靠。

標(biāo)記清除法

這個(gè)算法把“對(duì)象是否不再需要”簡(jiǎn)化定義為“對(duì)象是否可以獲得”沐旨。

從2012年起,所有現(xiàn)代瀏覽器都使用了標(biāo)記-清除垃圾回收算法榨婆。標(biāo)記清除法假定存在一個(gè)根對(duì)象(相當(dāng)于js的全局對(duì)象)磁携,垃圾回收器將定期從根對(duì)象開(kāi)始查找,凡是從根部出發(fā)能掃描到的都會(huì)保留良风,掃描不到的將被回收谊迄。

從根對(duì)象開(kāi)始掃描

1

右側(cè)的部分無(wú)法到達(dá),它將會(huì)被回收

2

就像是一桶水我們把它從根對(duì)象潑下去烟央,水流過(guò)的地方都沒(méi)事统诺,沒(méi)沾上水的對(duì)象就該回收了。

內(nèi)部流程

  1. 垃圾收集器找到所有的根疑俭,并“標(biāo)記”(記琢改亍)它們。
  2. 然后它遍歷并“標(biāo)記”來(lái)自它們的所有引用钞艇。
  3. 然后它遍歷標(biāo)記的對(duì)象并標(biāo)記 它們的 引用啄寡。所有被遍歷到的對(duì)象都會(huì)被記住,以免將來(lái)再次遍歷到同一個(gè)對(duì)象哩照。
  4. ……如此操作挺物,直到所有可達(dá)的(從根部)引用都被訪(fǎng)問(wèn)到。
  5. 沒(méi)有被標(biāo)記的對(duì)象都會(huì)被刪除飘弧。

幾種常見(jiàn)的內(nèi)存泄漏

  1. 全局變量

全局變量什么時(shí)候需要自動(dòng)釋放內(nèi)存空間很難判斷识藤,所以在開(kāi)發(fā)中盡量避免使用全局變量,以提高內(nèi)存有效使用率次伶。

  1. 未移除的事件綁定

dom元素雖然被移除了蹋岩,但元素綁定的事件還在,如果不及時(shí)移除事件綁定学少,在IE9以下版本容易導(dǎo)致內(nèi)存泄漏。現(xiàn)代瀏覽器不存在這個(gè)問(wèn)題了秧骑,了解一下即可版确。

let div = document.querySelector(".div");
let name = 'lee'
let handler = function () {
    console.log(name);
}
div.addEventListener('click', handler, false)

div.parentNode.removeChild(div) // 在IE9以下的老版本事件還在
  1. 無(wú)效的dom引用

有時(shí)候?qū)om作為對(duì)象的key存儲(chǔ)起來(lái)很有用扣囊,但是在不需要該dom時(shí),要記得及時(shí)解除對(duì)它的引用绒疗。

var ele = {
  node: document.getElementById('node')
};

document.body.removeChild(document.getElementById('node')); // 此時(shí)ele中還存在對(duì)node的引用
  1. 定時(shí)器setInterval/setTimeout

看下面的一段定時(shí)器代碼侵歇,一旦我們?cè)谄渌胤揭瞥薾ode節(jié)點(diǎn),定時(shí)器的回調(diào)便失去了意義吓蘑,然而它一直在執(zhí)行導(dǎo)致callback無(wú)法回收惕虑,進(jìn)而造成callback內(nèi)部掉數(shù)據(jù)resData也無(wú)法被回收。所以我們應(yīng)該及時(shí)clear定時(shí)器磨镶。

let resData = 100
let callback = function () {
    let node = document.querySelecter('.p')
    node && (node.innerHTML = resData)
}

setInterval (callback, 1000)

另外單獨(dú)說(shuō)一下閉包溃蔫,閉包和內(nèi)存泄漏沒(méi)有半毛錢(qián)關(guān)系,只是由于IE9之前的版本垃圾收集機(jī)制的原因琳猫,導(dǎo)致內(nèi)存無(wú)法進(jìn)行回收伟叛,這是IE的問(wèn)題,現(xiàn)代瀏覽器基本都不存在這個(gè)問(wèn)題脐嫂。當(dāng)然閉包要是使用不當(dāng)肯定是會(huì)造成內(nèi)存泄漏统刮。

WeakMap、WeakSet

es6的WeakMap和Map類(lèi)似账千,都是用于生成鍵值對(duì)的集合侥蒙,不同的是WeakMap是一種弱引用,它的鍵名所指向的對(duì)象匀奏,不計(jì)入垃圾回收機(jī)制鞭衩,另外就是WeakMap只接受對(duì)象作為鍵名(null除外),而Map可以接受各種類(lèi)型的數(shù)據(jù)作為鍵攒射。

WeakMap這種結(jié)構(gòu)有助于防止內(nèi)存泄漏醋旦,一旦消除對(duì)鍵的引用,它占用的內(nèi)存就會(huì)被垃圾回收機(jī)制釋放会放。WeakMap 保存的這個(gè)鍵值對(duì)饲齐,也會(huì)自動(dòng)消失。包括WeakSet也是類(lèi)似的咧最,內(nèi)部存儲(chǔ)的都是弱引用對(duì)象捂人,不會(huì)被計(jì)入垃圾回收。

看一個(gè)阮一峰ES6文檔上舉的例子:

let myWeakmap = new WeakMap();

myWeakmap.set(
  document.getElementById('logo'),
  {timesClicked: 0})
;

document.getElementById('logo').addEventListener('click', function() {
  let logoData = myWeakmap.get(document.getElementById('logo'));
  logoData.timesClicked++;
}, false);

上面代碼中矢沿,我們將dom對(duì)象作為鍵名滥搭,每次點(diǎn)擊,我們就更新一下?tīng)顟B(tài)捣鲸。我們將這個(gè)狀態(tài)作為鍵值放在WeakMap里瑟匆。一旦這個(gè)DOM節(jié)點(diǎn)刪除,該狀態(tài)就會(huì)自動(dòng)消失栽惶,不存在內(nèi)存泄漏風(fēng)險(xiǎn)愁溜。

WeakSet和WeakMap類(lèi)似疾嗅,它和set結(jié)構(gòu)的區(qū)別也是兩點(diǎn):

  1. WeakSet中的對(duì)象都是弱引用,不會(huì)被計(jì)入垃圾回收
  2. 成員只能是對(duì)象冕象,而不能是其他類(lèi)型的值

所以從垃圾回收的角度來(lái)看代承,合理的使用WeakMap和WeakSet,能幫助我們避免內(nèi)存泄漏渐扮。

小結(jié)

js的垃圾回收機(jī)制我們無(wú)法人為干預(yù)论悴,瀏覽器會(huì)定期巡查,js引擎在內(nèi)部做了很多優(yōu)化墓律,使其可以執(zhí)行的更快膀估,現(xiàn)代瀏覽器基本都采用標(biāo)記清楚的方法進(jìn)行垃圾回收。了解內(nèi)存泄漏的原因以及如何去規(guī)避只锻,防止翻車(chē)事故的發(fā)生??

參考資料: developer.mozilla.org玖像、javascript.info

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市齐饮,隨后出現(xiàn)的幾起案子捐寥,更是在濱河造成了極大的恐慌,老刑警劉巖祖驱,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件握恳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捺僻,警方通過(guò)查閱死者的電腦和手機(jī)乡洼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)匕坯,“玉大人束昵,你說(shuō)我怎么就攤上這事「鹁” “怎么了锹雏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)术奖。 經(jīng)常有香客問(wèn)我礁遵,道長(zhǎng),這世上最難降的妖魔是什么采记? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任佣耐,我火速辦了婚禮,結(jié)果婚禮上唧龄,老公的妹妹穿的比我還像新娘兼砖。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布掖鱼。 她就那樣靜靜地躺著然走,像睡著了一般。 火紅的嫁衣襯著肌膚如雪戏挡。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天晨仑,我揣著相機(jī)與錄音褐墅,去河邊找鬼。 笑死洪己,一個(gè)胖子當(dāng)著我的面吹牛妥凳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播答捕,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼逝钥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拱镐?” 一聲冷哼從身側(cè)響起艘款,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沃琅,沒(méi)想到半個(gè)月后哗咆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡益眉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年晌柬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郭脂。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡年碘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出展鸡,到底是詐尸還是另有隱情屿衅,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布娱颊,位于F島的核電站傲诵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏箱硕。R本人自食惡果不足惜拴竹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望剧罩。 院中可真熱鬧栓拜,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至啦鸣,卻和暖如春潮饱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诫给。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工香拉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人中狂。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓凫碌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親胃榕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盛险,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348