1. 程式人生 > 前端設計 >「前端面試100問」之JavaScript 記憶體管理和垃圾回收機制

「前端面試100問」之JavaScript 記憶體管理和垃圾回收機制

記憶體管理的主要目標是在需要時為系統提供動態分配的記憶體,然後釋放包含不再使用的物件的記憶體。 諸如C,C ++之類的語言具有諸如malloc()之類的用於記憶體分配的原始函式,其中某些高階語言(如JavaScript)內建了垃圾收集器來完成此工作。 它跟蹤程式的記憶體分配,並分享程式是否不再使用分配的記憶體,然後將其自動釋放。 但是這種演算法無法完全決定是否需要記憶體。 因此,對於程式設計師而言,理解並確定特定程式碼段是否需要記憶體非常重要。

接下來我們一起來了解垃圾回收如何在JavaScript中工作。

垃圾收集

JavaScript引擎的垃圾收集器基本上會尋找從記憶體中刪除的無法訪問的物件。 這篇文章解釋以下兩種常見的垃圾收集演算法:

  • 引用計數法
  • 標記清除法

引用計數法

這是一個簡單的垃圾收集演算法。 該演算法查詢那些沒有被引用的物件,如果物件沒有附加引用,則可以進行垃圾回收。

var obj1 = {
    property1: {
        subproperty1: 20
    }
};
複製程式碼

建立一個如上例所示的物件,以瞭解該演算法。

obj1指向了一個物件,其property1中還引用了另外一個物件。 由於obj1引用了property1指向的物件,因此此物件是不會被垃圾回收器進行回收的。

var obj2 = obj1;
obj1 = "some random text"
複製程式碼

現在,obj2

也具有了對obj1所引用的同一物件的引用,但是後來obj1的值被更新為“some random text”,所以現在只有obj2對引用著這個物件。

var obj_property1 = obj2.property1;
複製程式碼

接下來,obj_property1引用obj2.property1,該物件也包含一個物件。 因此,該物件現在具有兩個引用,如下所示:

  • 作為obj2的屬性
  • 變數obj_property1
obj2 = "some random text"
複製程式碼

obj2 被更新為 “some random text” 之後,其也不再引用這個物件。 因此,似乎這個物件沒有了其餘的引用,因此可以被垃圾回收了。 但這可能是錯誤的說法,因為obj_property1

具有obj2.property1的引用。 因此也不會被垃圾收集。

obj_property1 = null;
複製程式碼

現在,當我們從obj_property1中刪除引用時,原來位於obj1中的物件已完全沒有引用。 因此,現在可以對其進行垃圾收集了。

該演算法在哪些程式碼中會失效?

function example() {
     var obj1 = {
         property1 : {
              subproperty1: 20
         }
     };
     var obj2 = obj1.property1;
     obj2.property1 = obj1;
     return 'some random text'
}
example();
複製程式碼

在此,引用計數演算法不會在函式呼叫後從記憶體中刪除obj1obj2,因為這兩個物件相互引用

標記清除法

標記清除法會查詢從根(JavaScript的全域性物件)無法訪問的物件。

  • 該演算法克服了引用計數演算法的侷限性。

  • 沒有引用的物件將是不可訪問的,反之亦然。

var obj1 = {
     property1: 35
}
複製程式碼

如上所示,我們可以看到建立的物件obj1如何從ROOT可達。

obj1 = null
複製程式碼

現在,當我們將obj1的值設定為null時,該物件不再可以從ROOT到達,因此它將被進行垃圾回收。

預備知識

javascript中的記憶體管理是自動執行的,而且是不可見的
可達性:以某種方式可以訪問或者可以用的值,被保證儲存在記憶體中
根:有一些基本的固有可達值,由於顯而易見的原因無法刪除
    本地函式的區域性變數和引數
    當前巢狀呼叫鏈上的其他函式的變數和引數
    全域性變數
    其它...
可訪問性:如果引用或引用鏈可以從根訪問其他值,則認為該值是可訪問的
複製程式碼

標記-清除法 原理:

基本的垃圾回收演算法稱為“標記-清除”
垃圾回收器獲取根並“標記”它們
訪問並標記所有來自根的引用
訪問標記的物件並標記其引用。
以此類推,直到有未訪問的引用為止
除了標記物件,所有物件都被刪除
複製程式碼

讓我們通過檢視以下例項來嘗試和理解:

如上所示,這就是物件結構的樣子。 我們可以注意到從根目錄無法訪問的物件,但讓我們嘗試瞭解“標記並清除”演算法在這種情況下的工作方式。

演算法開始標記它從根開始可達的物件。 在上圖中,我們可以注意到在物件上標記的綠色圓圈。 以便將物件標識為從根可訪問。

那些從根目錄無法訪問的未標記的物件,它們將被垃圾收集。

注意

垃圾的定義

沒有被引用的物件或者有物件引用,但物件之間為相互引用,根訪問不到
複製程式碼

如上圖所示,儘管物件被引用了,但整體仍然是孤立的,最後仍然會被視為『垃圾』,並進行回收。

侷限性

必須使物件明確不可訪問,才會進行垃圾收集。

自2012年以來,JavaScript引擎針對引用計數垃圾收集演算法進行了修改。

補充:

JavaScript 引擎在垃圾回收方面的優化:

分代回收:物件分為“新物件”和“舊物件”,新物件出現,工作結束後就會被清理乾淨,存在時間長的物件,就會很少接受檢查

增量回收:將垃圾回收分解為多個部分,分別執行

空閒時間收集:垃圾回收器只在CPU空閒時執行
複製程式碼

謝謝閱讀。

參考