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