本文內容來自於對《垃圾回收的算法與實現》內容的總結算法
V8是啥編程
這個你們都知道,V8 全稱是V8 JavaScript Engine
,一個用C++寫的JavaScript
引擎。markdown
垃圾回收又是啥編程語言
垃圾回收的英文是 Garbage Collection
,簡稱GC
。在代碼運行的過程當中,全部的數據都會存放在內存空間,若是沒有GC
,開發者就必須手動進行內存管理,否則總有一天內存會被佔滿,進而致使進程崩潰,甚至系統崩潰。GC
的做用就是讓計算機自動幫開發者進行內存管理,把內存中不須要再次使用的垃圾回收掉。函數
本文講的只是V8
中的垃圾回收算法,而目前已有的垃圾回收算法遠不止這幾種,並且目前沒有完美的垃圾回收算法。spa
GC
時,相應的對象會被銷燬或者保留。這時候咱們能夠經過對象的指針去搜尋其餘對象。設計
指針通常指向對象的首地址。其實能夠簡單理解爲JS中的引用地址。3d
指動態存放對象的內存空間。JS中,對象存放在堆中。指針
mutator
的實體是「應用程序」code
我的理解,在JS的場景下,它指的就是V8引擎。在V8運行的過程當中,會不斷的去生成對象,修改對象之間的引用關係(即更新了指針)。這些改變就會帶來垃圾,這時候就須要GC
。
活動對象指堆中的能被mutator引用的對象。
非活動對象指堆中的不能被mutator引用的對象,即內存垃圾。
在下文中,屢次說起的一個名詞是根。什麼是根呢?術語上就是能夠經過mutator直接引用的對象。舉個例子
const obj = new Object(); // A對象
obj.child = new Object(); // B對象
複製代碼
第一行代碼中,咱們建立了一個對象A,它的引用地址賦值給了obj
。第二行代碼中,咱們又建立了一個對象,它的引用地址賦值給了obj.child
。那麼此時堆以下圖所示 這裏因爲咱們能夠經過全局變量
obj
找到A,因此A是活動對象,而後咱們又能夠經過A找到B,因此B也是活動對象。那麼這個全局變量obj
就是一個根。
將內存空間中的對象分爲兩類,新生代和老年代。新建立的對象存放在新生代中,通過新生代中的GC
後,某些對象依然存活。將存活了數次的對象看成老年代對象來處理。
新生代對象->老年代對象的過程,咱們稱之爲晉升。
GC
的分類GC
有兩種類型,保守式和準確式。
GC
(Conservative GC)當GC
時不能識別一個東西是否是指針時,這個時候的根被稱爲不明確的根。
舉個例子,咱們定義了全局變量a
和局部變量obj
,a
是一個數值,obj
是一個指針(引用地址),引用地址跟值a
同樣,這個時候GC
很難分辨出a
究竟是指針仍是值。因而,保守處理,把它當成一個指針,當obj
指向的對象應該被垃圾回收時,因爲全局變量a
的存在,它不會被垃圾回收。這就是保守式GC
。
window.a = 0x00d0caf0; // 僞代碼,固然是會報錯的
const obj = new Object(); // 建立了對象,地址爲0x00d0caf0
複製代碼
在保守式GC
的場景下,對象不可以被移動。由於若是移動了對象,意味着對象的引用地址會發生變化,那麼上面的obj
相應的會重寫成移動後的引用地址,與此同時,全局變量a
也會被重寫,這就很是恐怖了。因此對象不可以被移動。
GC
(Exact GC)顧名思義,準確式GC
可以正確識別出哪些內容是值,哪些內容是指針。要實現準確式GC
,依賴於編程語言的處理,意味着成本的增長,這裏再也不贅述。
V8
中的GC
V8
中實現了準確式GC
。
GC
算法方面採用了分代垃圾回收,結構以下。
GC
複製算法(By Cheney)GC
複製算法將內存空間分爲From
和To
,當From
空間佔滿時,將From
空間中的活動對象(劃重點)複製到To
空間中,非活動對象回收掉,而後From
和To
互換。顯而易見,From
和To
空間的大小要徹底一致。
GC
複製算法有不少種,好比 Robert R.Fenichel 和 Jerome C.Yochelson研究出來的和 C. J.Cheney 研究出來的。下面介紹的是Cheney研究出來的算法。
在Cheney的複製算法中,算法流程以下
初始狀態
首先,複製全部從根直接引用的對象,B
和 G
。注意,新的B
引用了From
中的A
,新的G
仍是在引用From
中的B
(爲區分,寫做B1
)和E
。
而後,搜索B1
,發現引用了A
,因而把A
複製到To
中,同時修正B
中的指向。
接着,搜索G
,把E
複製到To
中,而且G
指向B1
的指針換到了B
。
最後,搜索A
和E
,發現沒有引用的對象,清空From
,將From
和To
空間互換,複製算法結束。
GC
,由於移動了對象From
空間沒有分塊的時候
GC
標記-清除算法本算法分爲兩個階段
很明顯,通過這兩個階段後,不能利用的內存空得以再次被利用。
GC
的場景屢次GC
後會致使內存中出現碎片。碎片化的後果是,即便可用內存的總空間夠用,也會由於單個空間不夠用致使不可以分配內容(這個時候就要用到下面提到的標記-壓縮算法了)
假設內存空間一共5KB,下圖中A、B、C、D、E各佔了1KB,經歷一次GC
後,B和D被回收,內存空間中剩餘2KB,此時分配一個大小爲2KB的對象到內存空間中,沒法分配,由於剩餘的2KB空間並不連續。
GC
時會檢查)GC
時的晉升)GC
標記-壓縮算法標記階段:V8
採用深度優先的方式進行標記,即標記了對象,隨後會去標記這個對象的子對象。深度優先遍歷時,通常採用遞歸操做,遞歸時,天然須要用到棧,在V8
中,這個棧由V8
自行生成。棧所用的空間是新生代的From
空間。由於老年代GC
以前,必然會執行新生代GC
,這個時候From
空間是空的,既然空都空了,不如就把棧放在這裏,不用白不用xd。
壓縮階段:
壓縮前,能夠看到老年代空間中存在不少空白小方塊,即內存碎片
壓縮時,將內存對象按順序逐個移動到內存空間的前面
壓縮後,能夠看到空白小方塊已是連續的了,不存在內存碎片
一開始的時候,標題是《垃圾回收算法》,然而。。。瞭解深了以後發現,除了上面這些,還有引用計數法,增量式垃圾回收,RC Immix算法等等。即便是相同算法,不一樣設計者也有必定的區別。不一樣語言的GC
算法實現上也有必定的區別。內容多的離譜,因而在標題前面加上了「V8中的」。
這個文涉及的東西實際開發中並不常見,並且術語比較多,不免有地方寫錯了,有朋友發現了的話,麻煩評論區留個言。