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