1.垃圾是怎么來(lái)的
要介紹JavaScript的垃圾回收機(jī)制首先需要要知道垃圾是從何而來(lái)的。
每當(dāng)我們一個(gè)變量賦值時(shí),JavaScript都會(huì)為這個(gè)變量分配一塊存儲(chǔ)空間。當(dāng)為變量賦了一個(gè)值類型的變量時(shí),變量存儲(chǔ)在棧中,當(dāng)為變量賦了一個(gè)引用類型的值時(shí),變量將存儲(chǔ)在堆中。
在頁(yè)面打開并運(yùn)行時(shí),我們?cè)谑状渭虞d頁(yè)面和各種回調(diào)函數(shù)中會(huì)聲明很多變量,如果一直在堆棧中添加變量而不進(jìn)行回收,那么堆棧遲早會(huì)被壓滿,導(dǎo)致程序崩潰。而有些數(shù)據(jù)在使用之后就不再需要了,這些不需要的數(shù)據(jù)就是垃圾數(shù)據(jù)
現(xiàn)在我們知道我們必須清理內(nèi)存空間,將不需要的數(shù)據(jù)移除,那么怎么知道哪些數(shù)據(jù)不需要的?
2.如何判斷變量的值不可用
值類型的變量很簡(jiǎn)單,隨著函數(shù)的出棧,函數(shù)內(nèi)的變量也將被立即回收,閉包除外。
引用類型則是判斷變量的可達(dá)性,當(dāng)聲明一個(gè)引用類型的值時(shí),變量的值實(shí)際是一個(gè)指向變量值的內(nèi)存空間地址,例如:
function fn(){
var o = {a:1};
var ob = o;
}
fn();
…
此時(shí)變量o在fn函數(shù)中聲明并賦值,當(dāng)fn函數(shù)執(zhí)行完畢之后,變量o與ob的所在的執(zhí)行上下文都不可用,此時(shí)變量o的引用消失了。當(dāng)執(zhí)行垃圾回收時(shí)會(huì)從根對(duì)象開始遍歷(在瀏覽器環(huán)境是window,在node環(huán)境是global),將遍歷到的內(nèi)存空間標(biāo)記為不可回收,沒(méi)有遍歷到的內(nèi)存空間標(biāo)記為可回收。
題外話
因?yàn)槔厥諛?biāo)記對(duì)象時(shí)從根對(duì)象開始遍歷,所以函數(shù)中聲明并賦值的變量會(huì)在函數(shù)的執(zhí)行上下文銷毀時(shí),失去引用而被回收。而全局變量是永遠(yuǎn)都不會(huì)被回收的,因?yàn)閳?zhí)行上下文一直存在。
3. 垃圾是如何回收的
JavaScript將堆內(nèi)存分為兩塊區(qū)域,新生代與老生代。
新生代存放生存時(shí)間短的對(duì)象,新生代內(nèi)存較小,一般只有1-8M,新生代會(huì)把內(nèi)存空間一分為二,一半為對(duì)象區(qū)域,一半為空閑區(qū)域。使用副垃圾回收器進(jìn)行回收。
老生代存放生存時(shí)間長(zhǎng)的對(duì)象和較大的對(duì)象,使用主垃圾回收器進(jìn)行回收。
(1)副垃圾回收器
當(dāng)新生代內(nèi)存快要存滿時(shí),執(zhí)行一次副垃圾回收操作, 副垃圾回收器會(huì)先標(biāo)記對(duì)象區(qū)域中的活動(dòng)對(duì)象與非活動(dòng)對(duì)象,標(biāo)記完成之后將非活動(dòng)對(duì)象刪除,同時(shí)執(zhí)行對(duì)象晉升,即當(dāng)垃圾回收器執(zhí)行兩次之后對(duì)象依舊存活則將對(duì)象移動(dòng)到老生代。
刪除和對(duì)象晉升之后將剩余的對(duì)象復(fù)制到空閑區(qū)域并有序排列起來(lái),同時(shí)角色反轉(zhuǎn),空閑區(qū)域變成對(duì)象區(qū)域,對(duì)象區(qū)域清空變成空閑區(qū)域,循環(huán)往復(fù)。
(2)主垃圾回收器
主垃圾回收器同副垃圾回收器一樣,也會(huì)先執(zhí)行標(biāo)記,標(biāo)記完成之后會(huì)將存活的對(duì)象向內(nèi)存中的一端移動(dòng),然后直接刪除存活對(duì)象邊界外的所有對(duì)象
4.為何垃圾回收器都會(huì)對(duì)存活對(duì)象進(jìn)行整理
為一個(gè)對(duì)象賦值時(shí),值的大小是不確定的,而在垃圾回收后不對(duì)內(nèi)存空間進(jìn)行整理,那么刪除之后的空間是不連續(xù)的內(nèi)存碎片,無(wú)法得到有效的利用。例如我需要一塊較大的內(nèi)存時(shí),卻只有幾個(gè)小塊的內(nèi)存。整理之后就不存在這些內(nèi)存碎片了,可以對(duì)內(nèi)存空間進(jìn)行有效的利用。