簡述JavaScript的垃圾回收機制

無論是高級語言,仍是低級語言。內存的管理都是:node

  1. 分配內存
  2. 使用內存(讀或寫)
  3. 釋放內存

前兩步,你們都沒有太大異議。關鍵是釋放內存這一步,各類語言都有本身的垃圾回收(garbage collection, 簡稱GC)機制。作GC的第一步是判斷堆中存的是數據仍是指針,是指針的話,說明它被指向活躍的對象。有3種判斷方法:算法

  1. Conservative:若是存儲格式是地址,就認爲是。C/C++有用到這種算法。
  2. Compiler hints:對於靜態語言,好比Java,編譯器是知道它是否是指針的,因此能夠用這種。
  3. Tagged pointers:JavaScript用的是這種,在字末位進行標識,1爲指針。

對於JavaScript而言,最初的垃圾回收機制,是基於引用計次來作的。後來升級爲標記清除。瀏覽器

引用計次

當對象被引用次數爲0時,就被回收。潛在的一個問題是:循環引用時,兩個對象都至少被引用了一次,將不能自動被回收。因此致使,咱們常講的內存泄露。函數

// 引用計次
var a = {t: 1}; // 對象 `{t: 1}` (如下簡稱obj)被引用一次
var b = a; // obj 被引用兩次
a = null; // obj 如今爲1次
b = null; // obj 如今爲0次,可回收

// 循環引用
function fn() {
    var a = {};
    var b = {};
    a.b = b;
    b.a = a;
}

fn();

標記清除

這是當前主流的GC算法,V8裏面就是用這種。當對象,沒法從根對象沿着引用遍歷到,即不可達(unreachable),進行清除。對於上面的例子,fn() 裏面的 ab 在函數執行完畢後,就不能經過外面的上下文進行訪問了,因此就能夠清除了。post

下面,咱們簡述下V8的GC機制:spa

V8的GC機制

在大部分的應用場景:一個新建立的對象,生命週期一般很短。因此,V8裏面,GC處理分爲兩大類:新生代和老生代。指針

新生代的堆空間爲1M~8M,並且被平分紅兩份(to-space和from-space),一般一個新建立的對象,內存被分配在新生代。當to-space滿的時候,to-space和form-space交換位置(此時,to空,from滿),並執行GC.若是一個對象被判定爲,未被引用,就清除;有被引用,逃逸次數+1(若是此時逃逸次數爲2,就移入老生代,不然移入to-space)。code

老生代的堆空間大,GC不適合像新生代那樣,用平分紅兩個space這種空間換時間的方式。老生代的垃圾回收,分兩個階段:標記、清理(有Sweeping和Compacting這兩種方式)。orm

標記,採用3色標記:黑、白、灰。步驟以下:對象

  1. GC開始,因此對象標記爲白色。
  2. 根對象標記爲黑色,並開始遍歷其子節點(引用的對象)。
  3. 當前被遍歷的節點,標記爲灰色,被放入一個叫 marking bitmap 的棧。在棧中,把當前被遍歷的節點,標記爲黑色,並出棧,同時,把它的子節點(若是有的話)標記爲灰色,並壓入棧。(大對象比較特殊,這裏不展開)
  4. 當全部對象被遍歷完後,就只剩下黑和白。經過Sweeping或Compacting的方式,清理掉白色,完成GC。

小補充:JavaScript的根對象

GC的時候,從根對象開始遍歷。在瀏覽器,根對象是 window;在 Node.js 中,是 global(或稱爲root).

root.png

Node.js中,每一個文件被當作一個模塊,因此,當你用 var/let/const 在文件的全局,聲明變量的時候,做用域是當前文件(模塊)。所以,圖中 root.aundefined.

Links:

相關文章
相關標籤/搜索