前言
JavaScipt
和Java
一樣都是由垃圾回收機制進行自動內存管理,與C/C++
不同,JavaScript
不需要時刻關注內存的分配和釋放問題
由於Node
的出現,JavaScript
已不僅僅是一門瀏覽器的腳本語言,Node
極大地拓寬了JavaScript
的應用場景,從客戶端延申到服務器端之後,這使得JavaScript
使用者需要更加注意,在服務器端,內存管理的好壞將嚴重影響服務器的性能。
目前主流的JavaScript
引擎是由Google開發的V8引擎
,V8
性能優秀,使Google贏得了瀏覽器大戰。因此Node亦選用了V8。
V8的垃圾回收機制
主要的垃圾回收算法
V8的垃圾回數策略主要基於分代式垃圾回收機制
。分代式垃圾回收機制是指將對象按照存活時間
劃歸不同的世代,不同世代之後採用不同的回收算法。之所以這樣劃分是因為在本質上沒有任何一種回收算法能勝任所有場景
。
V8的內存分代主要分為新生代
和老生代
兩代。其中新生代是指存活時間較短
的對象,老生代中的對象則存活時間較長或常駐內存的對象
新生代
在分代的基础上,新生代中的對象主要通過Scavenge
算法進行垃圾回數。在Scavenge
的具體採用了Cherry
算法。
Cherry算法採用複制&移動
的方式實現垃圾回收。它將堆內存分為兩等份,每一部份的空間稱為semispace
,兩個semispace
只有一個處於使用中,另一個處理閑置狀態,使用中的叫做from空間
,閑置的叫做to空間
。
當開始進行垃圾回收時,先檢查From空間
中的存活對象,這些存活對象則會複制到To空間
,而非存活的對象則會被直接釋放。然後From空間
和To空間
的⻆色就會調換
對象晉升
當一個對象經歷過Scavenge
回收,又或者To空間的內存占用比超過限制,則該對象將會由新生代
晉升至老生代
。
老生代
對於老生代中的對象,V8主要採用Mark-Sweep
和Mark-Compact
相結合的方式進行垃圾回收。Mark-Sweep
是標記清除的意思,分為標記和清除兩個步驟,和Scavenge
不同,Mark-Sweep
不需要把內存劃分兩等份,在標記階段,Mark-Sweep
會遍歷堆中的所有對象,并標記活著的對象,在清除階段,則只會清除沒有標記的對象。
Mark-Sweep
會導致內存出現碎片化的現象,當需要分配一個大對象,會觸發一次不必要的垃圾回收。這時就有Mark-Compact
,它跟Mark-Sweep
差不多,區別只在對象在標記為死亡後,在整理的過程中,將活動的對象往一端移動,移動完成後,直接清理掉邊界外的內存。
V8主要使用Mark-Sweep
,在空間不足以對從新生代中晉升過來的對象進行分配時才使用Mark-Compact
3種垃圾回收算法的簡單對比
回數算法 | Mark-Sweep | Mark-Compact | Scavenge |
---|---|---|---|
速度 | 中等 | 最慢 | 最快 |
空間開銷 | 少(有碎片) | 少(無碎片) | 雙倍空間(無碎片) |
是否移動對象 | 否 | 是 | 是 |