這篇文章主要介紹了關於js的垃圾回收機制,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
記憶體管理於我們來說是自動的、看不見的。我們創建的原始類型、物件、函數等等,都會佔用記憶體。
當它們不被需要之後會發生什麼事? JavaScript 引擎要如何發現並清除他們?
JavaScript 記憶體管理的關鍵概念是可觸及(Reachability)。
簡單來說,「可觸及」的值就是可存取的,可用的,他們被安全地儲存在記憶體中。
以下是一些必定「可觸及」的值,不管出於任何原因,都不能刪除:
目前函數的局部變數和參數。
目前呼叫鏈(current chain of nested calls)中所有函數的局部變數和參數。
全域變數。
(以及其他內部變數)
这些值都称为 *roots*。
其他值是否可觸及視乎它是否被root 及其引用鏈引用。
假設有一個物件存在於局部變量,它的值引用了另一個對象,如果這個對像是可觸及的,則它引用的對像也是可觸及的,後面會有詳細例子。
JavaScript 引擎有一個 垃圾回收) 後台進程。它監控所有對象,當對像不可觸及時會將其刪除。
// user has a reference to the object let user = { name: "John" };
箭頭代表的是物件參考。全域變數 "user"
引用了物件{name: "John"}
(簡稱此物件為 John)。 John 的 "name"
屬性儲存的是一個原始值,所以沒有其他引用。
如果覆寫user
,對John 的引用就遺失了:
user = null;
現在John 變得不可觸及,垃圾回收機制會將其刪除並釋放記憶體。
如果我們從user
複製引用到admin
:
// user has a reference to the object let user = { name: "John" }; *!* let admin = user; */!*
如果重複這個動作:
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
函數讓兩個參數物件互相引用,傳回一個包含兩者的新對象,結構如下:
暫時所有物件都是可觸及的,但我們現在決定移除兩個引用:
delete family.father; delete family.mother.husband;
#只刪除一個引用不會有什麼影響,但兩個引用同時刪除,我們可以看到John 已經不被任何物件引用了:
即使John 還在引用別人,但是他不被別人引用,所以John 現在已經是不可觸及的了,它的存在將會被移除。
垃圾回收後:
也可能有一大堆互相引用的物件整塊(像個孤島)都不可觸及了。
對上面的物件進行操作:
family = null;
記憶體中的情況如下:
這個例子展示了「可觸及」這個概念的重要性。
儘管 John 和 Ann 互相依賴,但這仍不足夠。
"family"
物件整個已經切斷了與 root 的連接,沒有任何東西引用到這裡,所以這個孤島遙不可及,只能等待被刪除。
基礎的垃圾回收演算法稱為「標記-清除演算法」("mark-and-sweep"):
垃圾回收器取得並標記root。
然後訪問並標記來自他們的所有參考。
存取被標記的對象,標記他們的引用。所有被訪問過的物件都會被記錄,以後將不會重複存取同一物件。
…直到只剩下未造訪的參考。
所有未被標記的物件都會被移除。
假設物件結構如下:
#我們清楚地看到右邊的「孤島」。現在使用「標記-清除」的方法來處理他。
第一步,標記root:
然後標記他們的引用:
##… …標記他們引用的引用: 現在沒有被訪問過的物件會被認為是不可觸及,他們將會被刪除:#這就是垃圾回收的工作原理。 JavaScript 引擎在不影響執行的情況下做了很多優化,讓這個過程更垃圾回收效率更高:
-- 物件會被分成「新生代」和「老生代」。很多物件完成任務後很快就不再需要了,所以對於他們的清理可以很頻繁。而在清理中留下的稱為「老生代」一員。
-- 如果物件很多,很難一次標記完所有對象,這個過程甚至對程式執行產生了明顯的延遲。所以引擎會試著把這個操作分割成多份,每次執行一份。這樣做要記錄額外的數據,但是可以有效降低延遲對使用者體驗的影響。
-- 垃圾回收器盡量只在 CPU 空閒時執行,減少對程式執行的影響。
總結
如果你熟悉底層編程,可以閱讀 A tour of V8: Garbage Collection 以了解更多關於 V8 垃圾回收的細節。
V8 blog 也會經常發布一些關於記憶體管理的文章。學習垃圾回收演算法最好還是先學習 V8 的實現,閱讀 Vyacheslav Egorov(V8 工程師之一)的部落格。我說 V8 是因為在網路上關於 V8 的文章比較多。對於其他引擎,許多實作都是相似的,但是垃圾回收演算法上差別不少。
對引擎的深入理解在做底層優化的時候很有幫助。在你熟悉一門語言之後,這是一個明智的研究方向。
以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP中文網!
相關推薦:
如何透過js判斷頁面在pc端開啟還是行動端開啟以上是淺談一下js的垃圾回收的內容的詳細內容。更多資訊請關注PHP中文網其他相關文章!