V8 實(shí)現(xiàn)了準(zhǔn)確式 GC,GC 算法采用了分代式垃圾回收機(jī)制。因此,V8 將內(nèi)存(堆)分為新生代和老生代兩部分。
(1)新生代算法
新生代中的對(duì)象一般存活時(shí)間較短,使用 Scavenge GC 算法。
在新生代空間中,內(nèi)存空間分為兩部分,分別為 From 空間和 To 空間。在這兩個(gè)空間中,必定有一個(gè)空間是使用的,另一個(gè)空間是空閑的。新分配的對(duì)象會(huì)被放入 From 空間中,當(dāng) From 空間被占滿時(shí),新生代 GC 就會(huì)啟動(dòng)了。算法會(huì)檢查 From 空間中存活的對(duì)象并復(fù)制到 To 空間中,如果有失活的對(duì)象就會(huì)銷毀。當(dāng)復(fù)制完成后將 From 空間和 To 空間互換,這樣 GC 就結(jié)束了。
(2)老生代算法
老生代中的對(duì)象一般存活時(shí)間較長(zhǎng)且數(shù)量也多,使用了兩個(gè)算法,分別是標(biāo)記清除算法和標(biāo)記壓縮算法。
先來(lái)說(shuō)下什么情況下對(duì)象會(huì)出現(xiàn)在老生代空間中:新生代中的對(duì)象是否已經(jīng)經(jīng)歷過(guò)一次 Scavenge 算法,如果經(jīng)歷過(guò)的話,會(huì)將對(duì)象從新生代空間移到老生代空間中。To 空間的對(duì)象占比大小超過(guò) 25 %。在這種情況下,為了不影響到內(nèi)存分配,會(huì)將對(duì)象從新生代空間移到老生代空間中。老生代中的空間很復(fù)雜,有如下幾個(gè)空間
在老生代中,以下情況會(huì)先啟動(dòng)標(biāo)記清除算法:某一個(gè)空間沒(méi)有分塊的時(shí)候空間中被對(duì)象超過(guò)一定限制空間不能保證新生代中的對(duì)象移動(dòng)到老生代中在這個(gè)階段中,會(huì)遍歷堆中所有的對(duì)象,然后標(biāo)記活的對(duì)象,在標(biāo)記完成后,銷毀所有沒(méi)有被標(biāo)記的對(duì)象。在標(biāo)記大型對(duì)內(nèi)存時(shí),可能需要幾百毫秒才能完成一次標(biāo)記。這就會(huì)導(dǎo)致一些性能上的問(wèn)題。為了解決這個(gè)問(wèn)題,2011 年,V8 從 stop-the-world 標(biāo)記切換到增量標(biāo)志。在增量標(biāo)記期間,GC 將標(biāo)記工作分解為更小的模塊,可以讓 JS 應(yīng)用邏輯在模塊間隙執(zhí)行一會(huì),從而不至于讓應(yīng)用出現(xiàn)停頓情況。但在 2018 年,GC 技術(shù)又有了一個(gè)重大突破,這項(xiàng)技術(shù)名為并發(fā)標(biāo)記。該技術(shù)可以讓 GC 掃描和標(biāo)記對(duì)象時(shí),同時(shí)允許 JS 運(yùn)行。
清除對(duì)象后會(huì)造成堆內(nèi)存出現(xiàn)碎片的情況,當(dāng)碎片超過(guò)一定限制后會(huì)啟動(dòng)壓縮算法。在壓縮過(guò)程中,將活的對(duì)象向一端移動(dòng),直到所有對(duì)象都移動(dòng)完成然后清理掉不需要的內(nèi)存。