首頁 >web前端 >js教程 >淺談一下js的垃圾回收的內容

淺談一下js的垃圾回收的內容

不言
不言原創
2018-07-16 09:42:222252瀏覽

這篇文章主要介紹了關於js的垃圾回收機制,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

垃圾回收

記憶體管理於我們來說是自動的、看不見的。我們創建的原始類型、物件、函數等等,都會佔用記憶體。

當它們不被需要之後會發生什麼事? JavaScript 引擎要如何發現並清除他們?

可觸及(Reachability)

JavaScript 記憶體管理的關鍵概念是可觸及(Reachability)

簡單來說,「可觸及」的值就是可存取的,可用的,他們被安全地儲存在記憶體中。

  1. 以下是一些必定「可觸及」的值,不管出於任何原因,都不能刪除:

  • 目前函數的局部變數和參數。

  • 目前呼叫鏈(current chain of nested calls)中所有函數的局部變數和參數。

  • 全域變數。

  • (以及其他內部變數)

这些值都称为 *roots*。
  1. 其他值是否可觸及視乎它是否被root 及其引用鏈引用。

    假設有一個物件存在於局部變量,它的值引用了另一個對象,如果這個對像是可觸及的,則它引用的對像也是可觸及的,後面會有詳細例子。

JavaScript 引擎有一個 垃圾回收) 後台進程。它監控所有對象,當對像不可觸及時會將其刪除。

一個簡單例子

// user has a reference to the object
let user = {
  name: "John"
};

淺談一下js的垃圾回收的內容

箭頭代表的是物件參考。全域變數 "user" 引用了物件{name: "John"}(簡稱此物件為 John)。 John 的 "name" 屬性儲存的是一個原始值,所以沒有其他引用。

如果覆寫user,對John 的引用就遺失了:

user = null;

淺談一下js的垃圾回收的內容

現在John 變得不可觸及,垃圾回收機制會將其刪除並釋放記憶體。

兩個引用

如果我們從user 複製引用到admin

// user has a reference to the object
let user = {
  name: "John"
};

*!*
let admin = user;
*/!*

淺談一下js的垃圾回收的內容

如果重複這個動作:

user = null;

…這個物件是依然可以透過admin 存取的,所以它依然存在於記憶體。如果我們把 admin 也覆蓋為 null,那它就會被刪除了。

互相引用的物件

這個範例比較複雜:

function marry(man, woman) {
  woman.husband = man;
  man.wife = woman;

  return {
    father: man,
    mother: woman
  }
}

let family = marry({
  name: "John"
}, {
  name: "Ann"
});

marry 函數讓兩個參數物件互相引用,傳回一個包含兩者的新對象,結構如下:

淺談一下js的垃圾回收的內容

暫時所有物件都是可觸及的,但我們現在決定移除兩個引用:

delete family.father;
delete family.mother.husband;

淺談一下js的垃圾回收的內容

#只刪除一個引用不會有什麼影響,但兩個引用同時刪除,我們可以看到John 已經不被任何物件引用了:

淺談一下js的垃圾回收的內容

即使John 還在引用別人,但是他不被別人引用,所以John 現在已經是不可觸及的了,它的存在將會被移除。

垃圾回收後:

淺談一下js的垃圾回收的內容

孤島(Unreachable island)

也可能有一大堆互相引用的物件整塊(像個孤島)都不可觸及了。

對上面的物件進行操作:

family = null;

記憶體中的情況如下:

淺談一下js的垃圾回收的內容

這個例子展示了「可觸及」這個概念的重要性。

儘管 John 和 Ann 互相依賴,但這仍不足夠。

"family" 物件整個已經切斷了與 root 的連接,沒有任何東西引用到這裡,所以這個孤島遙不可及,只能等待被刪除。

內部演算法

基礎的垃圾回收演算法稱為「標記-清除演算法」("mark-and-sweep"):

  • 垃圾回收器取得並標記root。

  • 然後訪問並標記來自他們的所有參考。

  • 存取被標記的對象,標記他們的引用。所有被訪問過的物件都會被記錄,以後將不會重複存取同一物件。

  • …直到只剩下未造訪的參考。

  • 所有未被標記的物件都會被移除。

假設物件結構如下:

淺談一下js的垃圾回收的內容

#我們清楚地看到右邊的「孤島」。現在使用「標記-清除」的方法來處理他。

第一步,標記root:

淺談一下js的垃圾回收的內容

然後標記他們的引用:

淺談一下js的垃圾回收的內容

##… …標記他們引用的引用:

淺談一下js的垃圾回收的內容

現在沒有被訪問過的物件會被認為是不可觸及,他們將會被刪除:

淺談一下js的垃圾回收的內容

#這就是垃圾回收的工作原理。

JavaScript 引擎在不影響執行的情況下做了很多優化,讓這個過程更垃圾回收效率更高:

  • ##分代收集

    -- 物件會被分成「新生代」和「老生代」。很多物件完成任務後很快就不再需要了,所以對於他們的清理可以很頻繁。而在清理中留下的稱為「老生代」一員。

  • 增量收集

    -- 如果物件很多,很難一次標記完所有對象,這個過程甚至對程式執行產生了明顯的延遲。所以引擎會試著把這個操作分割成多份,每次執行一份。這樣做要記錄額外的數據,但是可以有效降低延遲對使用者體驗的影響。

  • 空閒時收集

    -- 垃圾回收器盡量只在 CPU 空閒時執行,減少對程式執行的影響。

  • 除此之外還有很多對垃圾回收的優化,在此就不詳細說了,各個引擎有自己的調整和技術,而且這個東西一直隨著引擎的更新換代在改變,如果不是有實在的需求,不值得挖太深。不過如果你真的對此有濃厚的興趣,以下會提供一些拓展連結給你。

總結

重點:

    垃圾回收自動進行,我們不能強制進行或阻止他。
  • 可觸及的物件會被保留在記憶體中。
  • 被引用不一定是可觸及的(從 root):相互引用的物件可能整塊都是不可觸及的。
  • 現代引擎實現了加強版的垃圾回收演算法,《The Garbage Collection Handbook: The Art of Automatic Memory Management》(R. Jones 等)一書中提及了他們。

如果你熟悉底層編程,可以閱讀 A tour of V8: Garbage Collection 以了解更多關於 V8 垃圾回收的細節。

V8 blog 也會經常發布一些關於記憶體管理的文章。學習垃圾回收演算法最好還是先學習 V8 的實現,閱讀 Vyacheslav Egorov(V8 工程師之一)的部落格。我說 V8 是因為在網路上關於 V8 的文章比較多。對於其他引擎,許多實作都是相似的,但是垃圾回收演算法上差別不少。

對引擎的深入理解在做底層優化的時候很有幫助。在你熟悉一門語言之後,這是一個明智的研究方向。

以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP中文網!

相關推薦:

如何透過js判斷頁面在pc端開啟還是行動端開啟


##對JS中的prototype的詳解

#

以上是淺談一下js的垃圾回收的內容的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn