JavaScript的垃圾回收機制

肥晨 發表於 2021-06-06
JavaScript

JavaScript是使用垃圾回收的語言,也就是很大的解決了跟蹤記憶體對開發者造成的負擔(畢竟這是很多問題的來源)。而卸下這個甜蜜的負擔(一點也不甜蜜好嘛),通過自動記憶體管理實現記憶體分配和閒置資源回收。

垃圾回收的基本思路:

JavaScript的垃圾回收機制

該過程是週期性的,每隔一個固定的時間,就會自動執行程式。這是一個不能稱之為完美的方案,畢竟演算法不能夠準確的知道,我們到底是否需要該變數。

下面以一個函式中區域性變數的正常生命週期為例。函式中的區域性變數會在函式執行時存在。

JavaScript的垃圾回收機制

 

 此時該變數已經不在需要,佔用的記憶體可以釋放,以供後者使用。所以垃圾回收機制需要跟蹤標記變數,並判定是否使用。如何標記未使用的變數也許有不同的實現方式。但是在瀏覽器裡面的話有兩種常用的方式:標記清理和引用計數。

 

標記清理

在JavaScript中最常用的垃圾回收策略是標記清理(mark-andsweep)。當變數進入上下文,比如:

JavaScript的垃圾回收機制

而不在上下文中的變數,邏輯上講,永遠不應該釋放它們的記憶體,因為只要上下文中的程式碼在執行,就有可能用到它們。

就算變數離開了上下文,也會做出標記。

JavaScript的垃圾回收機制

 給變數加標記的方式有很多種。比如,當變數進入上下文時,反轉某一位;或者可以維護“在上下文中”和“不在上下文中”兩個變數列 表,可以把變數從一個列表轉移到另一個列表。標記過程的實現並不重要,關鍵是策略。

垃圾回收程式執行的時候,會標記記憶體中儲存的所有變數。然後,它會將所有在上下文中的變數,以及被在上下文中的變數引用的變數的標記去掉。在此之後再被加上標記的變數就是待刪除的了,原因是任何在上下文中的變數都訪問不到它們了。隨後垃圾回收程式做一次記憶體清理,銷燬帶標記的所有值並收回它們的記憶體。

 

引用計數

上文提到的第二種垃圾回收機制。另一種沒那麼常用的垃圾回收策略是引用計數(reference counting)。

其思路是對每個值都記錄它被引用的次數。宣告變數並給它賦一個引用值時,這個值的引用數為1。如果同一個值又被賦給另一個變數,那麼引用數加1。類似地,如果儲存對該值引用的變數被其他 值給覆蓋了,那麼引用數減1。當一個值的引用數為0時,就說明沒辦 法再訪問到這個值了,因此可以安全地收回其記憶體了。垃圾回收程式 下次執行的時候就會釋放引用數為0的值的記憶體。

 

小結

JavaScript是使用垃圾回收的程式語言,開發者不需要操心記憶體分配和回收。JavaScript的垃圾回收程式可以總結如下。

離開作用域的值會被自動標記為可回收,然後在垃圾回收期間被刪除。

主流的垃圾回收演算法是標記清理,即先給當前不使用的值加上標記,再回來回收它們的記憶體。

引用計數是另一種垃圾回收策略,需要記錄值被引用了多少次。

JavaScript引擎不再使用這種演算法,但某些舊版本的IE仍然會受這種演算法的影響,原因是JavaScript會訪問非原生JavaScript物件(如 DOM元素)。

引用計數在程式碼中存在迴圈引用時會出現問題。

解除變數的引用不僅可以消除迴圈引用,而且對垃圾回收也有幫助。

為促進記憶體回收,全域性物件、全域性物件的屬性和迴圈引用都應該在不需要時解除引用