在不斷學習的過程中將學習到的知識整理成一個個知識模組,儘量保持其完整性與形成知識網路是編寫這些文字的初衷。
什麼是V8?
V8是一個開源的javascript引擎,基於C++編寫而成。谷歌瀏覽器就是基於V8引擎進行搭建的。同時node也使用了V8。
V8的新老空間記憶體分配與大小限制
新老空間
凡事都有一把雙刃劍,在垃圾回收的演變過程中人們發現,沒有一種特定的垃圾回收機制是可以完美的解決問題,因此V8採用了新生代與老生代結合的垃圾回收方式,將記憶體分為新生代和老生代。 新生代頻繁進行GC,空間小,採用的是空間換時間的scavenge演算法,所以又劃分為兩塊semispace,From和To。 老生代大部分儲存的是存活時間較長的或者較大的物件。採用的是mark-sweep(主)&mark-compact(輔)演算法。
V8限制了js物件可以使用的記憶體空間,不止是因為最初V8是作為瀏覽器引擎而設計的。還有其垃圾回收機制的影響因素。V8使用stop-the-world(全停頓), generational, accurate的垃圾回收器。在執行回收之時會暫時中斷程式的執行,而且只處理物件堆疊。當記憶體達到一定的體積時,進行一次垃圾回收的時間將會很長,從而影響其相應而造成瀏覽器假死的狀況。因此,在V8中限制老生代64位為1.4GB,32位為0.7GB,新生代64位為32M,32位為16M。 當然,如果需要更大的記憶體空間,在node中可以進行更改。
物件晉升
新生成的物件放入新生代記憶體中,那哪些物件會被放入老生代中呢?大部分放入老生代的物件是由新生代晉升而來。物件的晉升的方式:
-
當新生代的To semispace記憶體佔滿25%時,此時再從From semispace拷貝物件將不會再放入To空間中以防影響後續的新物件分配,而將其直接複製到老生代空間中。
-
在進行一次垃圾回收後,第二次GC時,發現已經經歷過一次GC的物件在從From空間複製時直接複製到老生代。
-
在新物件分配時大部分物件被分配到新生代的From semispace,但當這個物件的體積過大,超過1MB的記憶體頁時,直接分配到老生代中的large Object Space。
新生代的GC機制與優缺點
回收機制
新生代採用Scavenge演算法,在scavenge演算法的實現過程中,則主要採用了cheney演算法。即使用複製方式來實現垃圾回收。它將記憶體一分為二,每一個空間都是一個semispace。
處於使用狀態的是From空間,閒置的是To空間。當分配物件時,先是分配到From空間,垃圾回收時會檢查From空間中存活的物件,將其複製到To空間,回收其他的物件。完成複製後會進行緊縮,From和To空間的調換。如此迴圈往復。
優勢
由其執行的演算法及過程我們可以瞭解到,在新生代的垃圾回收過程中,總是由一半的semispace是空餘的。scavenge只複製存活的物件,在新生代的記憶體中,存活的物件相對較少,所以使用這個演算法恰到好處。
老生代的GC機制與優缺點
回收機制
由於的scavenge演算法只複製存活的物件,如果在老生代中也使用此演算法的話就會造成複製很多物件,效率低,並且造成很大的記憶體空間浪費。 老生代中採用的則是mark-sweep(標記清除)和mark-compact(標記整理)結合的方式。而為什麼使用兩者結合呢?這就要講到兩者的優點與缺點。
mark-sweep(標記清除)
-
優點
1.1 標記清除需要標記堆記憶體中的所有物件,標記出在使用的物件,清除那些沒有被標記的物件。在老生代記憶體中與新生代相反,不使用的物件只佔很小一部分,所以清除不用的物件效率高。
1.2.mark-sweep不會將記憶體空間分為兩半,所以,不會浪費一半空間。
-
缺點
但標記清除會造成一個問題,就是在清除過後會導致記憶體不連續,造成記憶體碎片,如果此時需要儲存一個很大的記憶體而空間又不夠的時候就會造成沒有必要的反覆垃圾回收。
mark-compact(標記整理)
-
優點
此時標記整理就可以出場了,在標記清除的過程中,標記整理會將存活的物件和需要清除的物件移動到兩端。然後將其中一段需要清除的消滅掉,可以解決標記清除造成的記憶體碎片問題。
-
缺點
但是在緊縮記憶體的過程中需要移動物件,效率比較低。所以V8在清理時主要會使用Mark-sweep,在空間不足以對新生代中晉升過來的物件進行分配時才會使用Mark-compact。
垃圾回收機制的優化
增量標記(在老空間裡引入了此方式)
scavenge演算法,mark-sweep及mark-compact都會導致stop-the-world(全停頓)。而全停頓很容易帶來明顯的程式遲滯,標記階段很容易就會超過100ms,因此V8引入了增量標記,將標記階段分為若干小步驟,每個步驟控制在5ms內,每執行一段時間標記動作,就讓JavaScript程式執行一會兒,如此交替,明顯地提高了程式流暢性,一定程度上避免了長時間卡頓。