天天看點

前端進階之垃圾回收機制

概念

原始資料類型是存儲在棧空間中的,引用類型的資料是存儲在堆空間中的。

不過有些資料被使用之後,可能就不再需要了,我們把這種資料稱為垃圾資料。

如果這些垃圾資料一直儲存在記憶體中,那麼記憶體會越用越多。

是以我們需要對這些垃圾資料進行回收,以釋放有限的記憶體空間。

這時候就引入了垃圾回收機制。

棧垃圾回收

當函數執行結束,JavaScript 引擎通過向下移動 

ESP

 指針(記錄調用棧目前執行狀态的指針),來銷毀該函數儲存在棧中的執行上下文(變量環境、詞法環境、

this

outer

)。

function fn(){
  var a = 1;
  var b = { age: '2' };
  function fn2(){
    var c = 3;
    var d = { name: 'xiaoming' };
  }
  fn2();
}
fn();
           

在上面代碼中,根據浏覽器政策:

  • 先到全局執行上下文
  • 再到 foo 函數執行上下文
  • 最後到 showName 函數執行上下文

根據棧的特性,此時 JavaScript 情況應為:

調用棧

fn2

 函數執行上下文
fn函數執行上下文
全局執行上下文

釋放過程:

  1. 執行到 fn2 函數的時候,建立 fn2 函數的執行上下文,并将 fn2 函數的執行上下文壓入棧中。
  2. 以此同時,我們會有一個記錄目前執行狀态的指針(稱為 ESP),指向調用棧中 fn2 函數的執行上下文,表示目前正在執行 fn2 函數。
  3. 當 fn2 函數執行完畢,函數執行流程進入 fn 函數,這時候 ESP 就下移到 fn 函數的執行上下文。這個下移操作就是銷毀 fn2 函數執行上下文的過程。

如果 fn2 函數下面還調用了另一個函數,那麼它會覆寫 fn2 函數的内容,用來存放另一個函數的執行上下文。

就是說:當一個函數執行結束之後,JavaScript 引擎會通過向下移動 

ESP

 來銷毀儲存棧中的執行上下文。

堆垃圾回收

上述代碼中的對象依舊存在堆中要回收對象的垃圾資料,就需要 JavaScript 中的垃圾回收器。

V8 中會把堆分為 新生代 和 老生代 兩個區域,新生代中存放的是生存時間短的對象,老生代中存放的生存時間久的對象。

新生區的容量沒有老生區那麼大,是以 V8 分别使用 2 個不同的垃圾回收器,來高效實施垃圾回收:

  • 副垃圾回收器:主要負責新生代的垃圾回收。
  • 主垃圾回收器:主要負責老生代的垃圾回收。

新生代 - 副垃圾回收器

算法:Scavenge 算法。

原理:

  • 把新生代空間對半劃分為兩個區域,一半是對象區域,一半是空閑區域。
  • 新加入的對象都會存放到對象區域,當對象區域快被寫滿時,就需要執行一次垃圾清理操作。
  • 先對對象區域中的垃圾做标記,标記完成之後,把這些存活的對象複制到空閑區域中
  • 完成複制後,對象區域與空閑區域進行角色翻轉,也就是原來的對象區域變成空閑區域,原來的空閑區域變成了對象區域。

對象晉升政策:經過兩次垃圾回收依然還存活的對象,會被移動到老生區中。

老生代 - 主垃圾回收器

算法:标記 - 清除(Mark-Sweep)算法。

原理:

  1. 标記:标記階段就是從一組根元素開始,遞歸周遊這組根元素,在這個周遊過程中,能到達的元素稱為活動對象,沒有到達的元素就可以判斷為垃圾資料。
  2. 清除:将垃圾資料進行清除。

對一塊記憶體多次執行标記 - 清除算法後,會産生大量不連續的記憶體碎片。而碎片過多會導緻大對象無法配置設定到足夠的連續記憶體。

這時候就需要标記整理。

算法:标記 - 整理(Mark-Compact)算法

原理:

  1. 标記:和标記 - 清除的标記過程一樣,從一組根元素開始,遞歸周遊這組根元素,在這個周遊過程中,能到達的元素标記為活動對象。
  2. 整理:讓所有存活的對象都向記憶體的一端移動
  3. 清除:清理掉端邊界以外的記憶體

全停頓

由于 JavaScript 是單線程的,是以一旦執行垃圾回收算法,那正在執行的 JavaScript 腳本需要暫停下來,等垃圾回收完畢之後再恢複腳本執行。

這種行為叫 全停頓(Stop-The-World)。

如果堆中的資料較多,那麼回收需要時間,會造成頁面卡頓狀态,是以為了降低這個卡頓,V8 将标記過程分為一個一個子标記過程,同時垃圾回收标記和 JavaScript 應用邏輯交替進行。

優化算法:增量标記(Incremental Marking)算法

原理:

  1. 為了降低老生代的垃圾回收而造成的卡頓
  2. V8 把一個完整的垃圾回收任務拆分為很多小的任務
  3. 讓垃圾回收标記和 JavaScript 應用邏輯交替進行