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