垃圾收集機制

一、引言

JavaScript具有自動垃圾收集機制悴了,即執(zhí)行環(huán)境會負(fù)責(zé)管理代碼執(zhí)行過程中使用的內(nèi)存煞肾。

垃圾收集機制的原理:垃圾回收機制就是垃圾收集器周期性地找到不再使用的變量,并釋放掉它們所指向的內(nèi)存囤捻。

JS的垃圾回收機制是為了以防內(nèi)存泄漏。不再用到的內(nèi)存邻寿,沒有及時釋放蝎土,就叫做內(nèi)存泄漏视哑,即當(dāng)已經(jīng)不需要某塊內(nèi)存時這塊內(nèi)存還存在著。通俗點說就是誊涯,這個內(nèi)存該清掉挡毅,但是沒有被清掉,就造成了內(nèi)存泄露暴构。

程序的運行需要內(nèi)存跪呈,只要程序提出要求,操作系統(tǒng)就必須供給內(nèi)存丹壕。對于持續(xù)運行的服務(wù)進(jìn)程庆械,必須及時釋放內(nèi)存,否則菌赖,內(nèi)存占用越來越高缭乘,輕則影響系統(tǒng)性能,重則導(dǎo)致進(jìn)程崩潰琉用。

通常有兩個用于標(biāo)識無用變量的策略(垃圾收集方式):標(biāo)記清除堕绩、引用計數(shù)。

三邑时、標(biāo)記清除(js中最常用的垃圾收集方式)

當(dāng)變量進(jìn)入環(huán)境(如在函數(shù)中聲明一個變量)奴紧,就標(biāo)記這個變量為“進(jìn)入環(huán)境”。當(dāng)變量離開環(huán)境晶丘,則將其標(biāo)記為“離開環(huán)境”黍氮。

標(biāo)記清除:垃圾收集器先給存儲在內(nèi)存中的所有對象加上標(biāo)記,然后去掉環(huán)境中的變量和被環(huán)境中的變量引用的對象的標(biāo)記浅浮,剩下的被標(biāo)記的對象就被視為準(zhǔn)備刪除的變量沫浆,原因是環(huán)境中的變量已經(jīng)無法訪問到這些變量了。最后滚秩。垃圾收集器完成內(nèi)存清除工作专执,銷毀那些帶標(biāo)記的值,并回收他們所占用的內(nèi)存空間郁油。

二本股、引用計數(shù)

引用計數(shù)的含義是跟蹤記錄每個值被引用的次數(shù)。

引用計數(shù):當(dāng)聲明了一個變量并將一個引用類型值賦給該變量時桐腌,則這個值的引用次數(shù)就是1拄显。如果同一個值又被賦給另一個變量,則該值的引用次數(shù)加1案站。相反躬审,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數(shù)就減1。當(dāng)這個引用次數(shù)變成0時盒件,則說明沒有辦法再訪問這個值了,因而就可以將其所占的內(nèi)存空間給收回來舱禽。這樣炒刁,垃圾收集器下次再運行時,它就會釋放那些引用次數(shù)為0的值所占的內(nèi)存誊稚。

但是當(dāng)對象循環(huán)引用時翔始,會導(dǎo)致引用次數(shù)永遠(yuǎn)無法歸零,造成內(nèi)存無法釋放里伯。循環(huán)引用指的是:對象A中包含一個指向?qū)ο驜的指針城瞎,而對象B中也包含一個指向?qū)ο驛的引用,例如:

function problem(){
    var objectA = new Object();
    var objectB = new Object();
    
    objectA.someOtherObject = objectB;
    objectB.anOtherObject = objectA;
}
// 對象A和B的引用次數(shù)都是2疾瓮,內(nèi)存得不到回收脖镀。

由于低版本IE中的BOM和DOM的對象就是使用C++以COM(Component Object Model,組件對象模型)對象的形式實現(xiàn)的狼电,而COM對象的垃圾收集機制采用的就是引用計數(shù)策略蜒灰。 因此,即使IE的JavaScript引擎是使用標(biāo)記清除策略來實現(xiàn)的肩碟,但JavaScript訪問的COM對象依然是基于引用計數(shù)策略的强窖。換句話說,只要在IE中涉及COM對象削祈,就會存在循環(huán)引用的問題翅溺。

為了解決這個問題,IE9把BOM和DOM對象都轉(zhuǎn)換成了真正的JavaScript對象髓抑,就避免了兩種垃圾收集算法并存導(dǎo)致的問題咙崎,也消除了常見的內(nèi)存泄露現(xiàn)象。

三启昧、V8如何進(jìn)行垃圾回收

[圖片上傳失敗...(image-2c0212-1597281228800)]

棧內(nèi)存的回收:

棧內(nèi)存調(diào)用棧上下文切換后就被回收叙凡,比較簡單。

函數(shù)執(zhí)行完密末,形成的執(zhí)行上下文中握爷,沒有東西被上下文以外的內(nèi)容占用,此上下文就會從執(zhí)行環(huán)境棧中移除(釋放)严里,如果有被占用新啼,則壓縮到棧的底部(沒有釋放,就形成閉包)刹碾。

堆內(nèi)存的回收:變量 = null

V8的堆內(nèi)存分為新生代內(nèi)存和老生代內(nèi)存燥撞,新生代內(nèi)存是臨時分配的內(nèi)存,存在時間短,老生代內(nèi)存存在時間長物舒。

新生代內(nèi)存回收機制:

  • 新生代內(nèi)存容量小色洞,64位系統(tǒng)下僅有32M。新生代內(nèi)存分為From冠胯、To兩部分火诸,進(jìn)行垃圾回收時,先掃描From荠察,將非存活對象回收置蜀,將存活對象順序復(fù)制到To中,之后調(diào)換From/To悉盆,等待下一次回收

老生代內(nèi)存回收機制

  • 晉升:如果新生代的變量經(jīng)過多次回收依然存在盯荤,那么就會被放入老生代內(nèi)存中
  • 標(biāo)記清除:老生代內(nèi)存會先遍歷所有對象并打上標(biāo)記,然后對正在使用或被強引用的對象取消標(biāo)記焕盟,回收被標(biāo)記的對象
  • 整理內(nèi)存碎片:把對象挪到內(nèi)存的一端

四秋秤、管理內(nèi)存

確保占用最少的內(nèi)存可以讓頁面獲得更好的性能。而優(yōu)化內(nèi)存占用的最佳方式脚翘,就是為執(zhí)行中的代碼只保存必要的數(shù)據(jù)航缀。一旦數(shù)據(jù)不再有用,最好通過將其值設(shè)置為null來釋放其引用(解除引用)堰怨,這一做法適用于大多數(shù)全局變量和全局對象的屬性芥玉。解除引用的真正作用是讓值脫離執(zhí)行環(huán)境,以便垃圾收集器下次運行時將其回收(并不意味著解除就自動回收該值所占用的內(nèi)存)备图。

function createPerson(name){
    var localPerson = new Object();
    localPerson.name = name;
    return localPerson;
}

var globalPerson = createPerson('Shine')灿巧;

// 手工解除全局變量globalPerson的引用
globalPerson = null;

五揽涮、垃圾收集與閉包

閉包可以避免全局變量的污染抠藕,但是如果閉包使用過多,就會使得很多局部變量常駐內(nèi)存蒋困,增加了內(nèi)存的開銷盾似。濫用閉包在IE中可能會造成內(nèi)存泄露。

沒有產(chǎn)生閉包的情況:

當(dāng)fn()執(zhí)行完之后雪标,函數(shù)內(nèi)部的局部變量a以及局部函數(shù)就銷毀了零院。則每執(zhí)行一次,局部變量和局部函數(shù)都是重新定義的村刨,執(zhí)行完畢后告抄,就會被垃圾收集器回收。

即沒有閉包的情況下嵌牺,執(zhí)行完fn()打洼,a會自動釋放龄糊。

function fn(){
    var a = 1;
    return function(){
        return a++;
    }
}
console.log(fn());    // ? () { return a++; }
console.log(fn()());  // 1
console.log(fn()());  // 1

產(chǎn)生閉包的情況:

fn函數(shù)每次執(zhí)行,都會形成一個新的環(huán)境募疮,這個新的環(huán)境被全局變量test保存下來炫惩,test和子函數(shù)建立了引用關(guān)系,子函數(shù)和父函數(shù)中的局部變量又存在引用關(guān)系阿浓。

由于兩個以上存在引用關(guān)系的對象诡必,只要有一個是全局的,那么其他的就不會被回收搔扁。由于test是全局的,因此a不會被釋放蟋字。

function fn(){
    var a = 1;
    return function(){
        return a++;
    }
}
// 在父函數(shù)的外部稿蹲,調(diào)用其局部變量,聲明一個全局變量test來接收父函數(shù)執(zhí)行后返回的匿名函數(shù)
var test = fn();    
console.log(test);  // ? () { return a++; }
console.log(test()); // 1
console.log(test()); // 2鹊奖, fn每次執(zhí)行都會形成一個新的環(huán)境苛聘,也稱為閉包環(huán)境

當(dāng)包含閉包的對象成為垃圾對象,即失去引用test = null忠聚,閉包中涉及的變量a再也沒有被引用设哗,閉包死亡。

解除引用后两蟀,就會讓a脫離執(zhí)行環(huán)境网梢,以便垃圾收集器下次運行時將其回收。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赂毯,一起剝皮案震驚了整個濱河市战虏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌党涕,老刑警劉巖烦感,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異膛堤,居然都是意外死亡手趣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門肥荔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绿渣,“玉大人,你說我怎么就攤上這事燕耿∏釉危” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵缸棵,是天一觀的道長舟茶。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么吧凉? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任隧出,我火速辦了婚禮,結(jié)果婚禮上阀捅,老公的妹妹穿的比我還像新娘胀瞪。我一直安慰自己,他們只是感情好饲鄙,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布凄诞。 她就那樣靜靜地躺著,像睡著了一般忍级。 火紅的嫁衣襯著肌膚如雪帆谍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天轴咱,我揣著相機與錄音汛蝙,去河邊找鬼。 笑死朴肺,一個胖子當(dāng)著我的面吹牛窖剑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播戈稿,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼西土,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鞍盗?” 一聲冷哼從身側(cè)響起翠储,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎橡疼,沒想到半個月后援所,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡欣除,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年住拭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片历帚。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡滔岳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挽牢,到底是詐尸還是另有隱情谱煤,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布禽拔,位于F島的核電站刘离,受9級特大地震影響室叉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硫惕,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一茧痕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恼除,春花似錦踪旷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至徽级,卻和暖如春气破,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背灰追。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狗超,地道東北人弹澎。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像努咐,于是被迫代替她去往敵國和親苦蒿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354