文章來源于公眾號:前端Symbol盧 ,作者Symbol盧
前言
大多數(shù)語言都是提供自動內(nèi)存管理機制,比如 C# 、Java ,JavaScript 。自動內(nèi)存管理機制也就是我們經(jīng)常聽到的垃圾回收機制 。好神奇哦,語言會收垃圾,哈哈,不過這里的垃圾,可不是家里面的廚余垃圾啥的,而是 一些不再使用的變量所占用的內(nèi)存。我們的 JavaScript 的執(zhí)行環(huán)境會自動對這些垃圾進行回收,也就是釋放那些不再使用的變量所占用的內(nèi)存,收垃圾的過程 會按照固定的時間間隔周期性的執(zhí)行 垃圾回收
內(nèi)存泄露
如果 那些不再使用的變量所占用的內(nèi)存 沒有被釋放 會怎樣呢??? 那就會造成 內(nèi)存泄露
什么是內(nèi)存泄露???別著急往下看
內(nèi)存泄露其實就是我們的程序中已經(jīng)動態(tài)分配的堆內(nèi)存,由于某些原因沒有得到釋放,造成系統(tǒng)內(nèi)存的浪費導(dǎo)致程序運行速度減慢甚至系統(tǒng)崩潰等嚴重后果。后果很嚴重的哦,現(xiàn)在知道為啥要有垃圾回收機制了嘛
垃圾回收機制
看看這個代碼可以加深理解垃圾回收機制哦?
let name = '編程獅';
function aboutMe() {
let hobby = '吃肉肉';
let say=`我是${name},我喜歡${hobby}`;
console.log(say);
}
aboutMe()
在函數(shù) aboutMe()
執(zhí)行的時候,聲明了兩個局部的變量 hobby
和say
,等函數(shù)執(zhí)行完畢之后,這兩個局部變量也就不再使用 ,所以垃圾回收機制 就會將不再使用的(局部)變量 hobby
和say
清除掉 (釋放了它們的內(nèi)存)
在 JavaScript 中能實現(xiàn)這樣的垃圾回收的功能的一共有兩種方式:標(biāo)記清除 和引用計數(shù)
標(biāo)記清除
標(biāo)記清除是js中最常用的垃圾回收方式。它的原理也比較好理解,廢話不多說,先上代碼
function speakLines(){
let night="天黑";//做個標(biāo)記 ,進入環(huán)境
let closeEyes="閉眼";//做個標(biāo)記 ,進入環(huán)境
let speak=`開始狼人殺,${night}請${closeEyes}`;//做個標(biāo)記 ,進入環(huán)境
console.log(speak);
}
speakLines() //代碼執(zhí)行完畢 里面被標(biāo)記過的變量,又被標(biāo)記 離開環(huán)境 最后被回收
當(dāng)代碼執(zhí)行在一個環(huán)境中時(例如上面的 speakLines
函數(shù)),每聲明一個變量,就會對該變量做一個標(biāo)記(例如標(biāo)記一個進入執(zhí)行環(huán)境);當(dāng)代碼執(zhí)行進入另一個環(huán)境中時,也就是要離開上一個環(huán)境( speakLines
函數(shù)執(zhí)行完畢,去執(zhí)行其他的函數(shù)),這時對上一個環(huán)境中的變量做一個標(biāo)記,(例如標(biāo)記一個離開執(zhí)行環(huán)境),等到垃圾回收執(zhí)行時,會根據(jù)標(biāo)記來決定要清除哪些變量進行釋放內(nèi)存
引用計數(shù)
引用計數(shù)是一種不太常用的垃圾回收方式。同樣也是先上代碼再說原理
let skill = ["唱歌", "彈吉他", "播音"]
function change() {
let new_skill = ["vue", "react", "node", "webpack", ".net", "mysql", "sqlServer", "nginx"];
skill = new_skill;
//一個文藝小青年就這樣的變成了一個技術(shù)宅男
}
change()
console.log(skill) //返回 ["vue", "react", "node", "webpack", ".net", "mysql", "sqlServer", "nginx"]
change()
內(nèi)部聲明了一個局部變量 new_skill
,并將一個引用類型 的數(shù)組 賦值給它,同時又將變量new_skill
賦值給了全局變量 skill
,此時,這個局部變量 new_skill
就不會被當(dāng)成垃圾回收了。啊,為什么呢??? 還記得我們在上面說過的 垃圾回收,回收的是那些不再使用的變量,釋放它們的內(nèi)存,因為此時的局部變量 new_skill
并不是一個不再使用 的局部變量了,它被全局變量 skill
所引用了( 還在使用),所以就不會被回收了。
上面的這一過程,使用的就是引用計數(shù)的垃圾回收方式
引用計數(shù)的策略是跟蹤記錄每個值被使用的次數(shù),當(dāng)聲明了一個變量并將一個引用類型賦值給該變量的時候這個值的引用次數(shù)就加1,如果該變量的值變成了另外一個,則這個值的引用次數(shù)減1,當(dāng)這個值的引用次數(shù)變?yōu)?的時候,說明沒有變量在使用,這個值沒法被訪問了,因此可以將其占用的空間回收,當(dāng)垃圾回收的時候,就會將 引用次數(shù)為0的進行回收,釋放對應(yīng)的內(nèi)存
let skill = ["唱歌", "彈吉他", "播音"]
function change() {
let new_skill = ["vue", "react", "node", "webpack", ".net", "mysql", "sqlServer", "nginx"];//被引用次數(shù)為0
skill = new_skill; ////被引用次數(shù)為1
}
change()
console.log(skill)
管理內(nèi)存
在我們的項目中,我們肯定是想要用更少的內(nèi)存,來使得頁面有更好的性能。這個時候就需要我們來手動的管理內(nèi)存了,及時的去釋放數(shù)據(jù)的引用。我們可以只將需要的數(shù)據(jù),存入變量。一旦這個數(shù)據(jù)不再使用了,我們需要手動的給變量賦值 null
來釋放數(shù)據(jù)的引用。(解除引用) 大多需要我們進行手動的解除引用的都是一些全局的變量,因為局部的變量,在離開環(huán)境的時候就會被自動清除了。
let skill = ["唱歌", "彈吉他", "播音"]
function change() {
let new_skill = ["vue", "react", "node", "webpack", ".net", "mysql", "sqlServer", "nginx"]
skill = new_skill;
//一個文藝小青年就這樣的變成了一個技術(shù)宅男
}
change()
console.log(skill)
skill = null;//將不再使用的變量進行賦值 unll 來釋放數(shù)據(jù)引用
change()
內(nèi)部聲明的局部變量 new_skill
被全局變量 skill
所引用,所以此時變量 new_skill
的引用次數(shù)為1,為了讓變量 new_skill
被清除引用,在代碼的最后一行,賦值一個 null
給全局變量 skill
,手動解除了變量 skill
對變量 new_skill
的引用,此時變量 new_skill
的引用次數(shù) 減1,所以 當(dāng)前 new_skill
的引用次數(shù)為0了。當(dāng)垃圾回收機制執(zhí)行的時候,發(fā)現(xiàn) new_skill
的引用次數(shù)為 0,就把該變量當(dāng)成無用變量給清除了,釋放了內(nèi)存,提高了性能。
以上就是W3Cschool編程獅
關(guān)于js垃圾回收機制原理給你聊的明明白白的相關(guān)介紹了,希望對大家有所幫助。