JS垃圾回收機制

為了保證的可讀性,本文采用意譯而非直譯。

最近看到一些面試的回顧,不少有被面試官問到談?wù)凧S 垃圾回收機制,說實話,面試官會問這個問題,說明他最近看到一些關(guān)于 JS 垃圾回收機制的相關(guān)的文章,為了 B 格,就會順帶的問問。

最近看到一篇講 JS 垃圾回收的國外文章,覺得講得明白,所以就翻譯過來了,希望對你們有所幫助。

垃圾回收

JavaScript 中的內(nèi)存管理是自動執(zhí)行的,而且是不可見的。我們創(chuàng)建基本類型、對象、函數(shù)……所有這些都需要內(nèi)存。

當(dāng)不再需要某樣?xùn)|西時會發(fā)生什么? JavaScript 引擎是如何發(fā)現(xiàn)并清理它?

可達性

JavaScript 中內(nèi)存管理的主要概念是可達性。

簡單地說,“可達性” 值就是那些以某種方式可訪問或可用的值,它們被保證存儲在內(nèi)存中。

1. 有一組基本的固有可達值,由于顯而易見的原因無法刪除。例如:

本地函數(shù)的局部變量和參數(shù)

當(dāng)前嵌套調(diào)用鏈上的其他函數(shù)的變量和參數(shù)

全局變量

還有一些其他的,內(nèi)部的

這些值稱為根。

2. 如果引用或引用鏈可以從根訪問任何其他值,則認為該值是可訪問的。

例如,如果局部變量中有對象,并且該對象具有引用另一個對象的屬性,則該對象被視為可達性, 它引用的那些也是可以訪問的,詳細的例子如下。

JavaScript 引擎中有一個后臺進程稱為垃圾回收器,它監(jiān)視所有對象,并刪除那些不可訪問的對象。

一個簡單的例子

下面是最簡單的例子:

//user具有對象的引用

letuser= {

? name:"John"};


這里箭頭表示一個對象引用。全局變量“user”引用對象?{name:“John”}?(為了簡潔起見,我們將其命名為John)。John 的?“name”?屬性存儲一個基本類型,因此它被繪制在對象中。

如果?user?的值被覆蓋,則引用丟失:

user= null;


現(xiàn)在?John?變成不可達的狀態(tài),沒有辦法訪問它,沒有對它的引用。垃圾回收器將丟棄?John?數(shù)據(jù)并釋放內(nèi)存。

兩個引用

現(xiàn)在讓我們假設(shè)我們將引用從?user?復(fù)制到?admin:

//user具有對象的引用letuser= {name: "John"};letadmin=user;


現(xiàn)在如果我們做同樣的事情:

user= null;

該對象仍然可以通過?admin?全局變量訪問,所以它在內(nèi)存中。如果我們也覆蓋admin,那么它可以被釋放。

相互關(guān)聯(lián)的對象

現(xiàn)在來看一個更復(fù)雜的例子, family 對象:

function marry (man,woman) {woman.husban =man;man.wife =woman;? return {? ? father:man,? ? mother:woman}}let family = marry({? name:"John"}, {? name:"Ann"})

函數(shù)?marry?通過給兩個對象彼此提供引用來“聯(lián)姻”它們,并返回一個包含兩個對象的新對象。

產(chǎn)生的內(nèi)存結(jié)構(gòu):


到目前為止,所有對象都是可訪問的。

現(xiàn)在讓我們刪除兩個引用:

deletefamily.father;deletefamily.mother.husband;


僅僅刪除這兩個引用中的一個是不夠的,因為所有對象仍然是可訪問的。

但是如果我們把這兩個都刪除,那么我們可以看到?John?不再有傳入的引用:


輸出引用無關(guān)緊要。只有傳入的對象才能使對象可訪問,因此,John?現(xiàn)在是不可訪問的,并將從內(nèi)存中刪除所有不可訪問的數(shù)據(jù)。

垃圾回收之后:


無法訪問的數(shù)據(jù)塊

有可能整個相互連接的對象變得不可訪問并從內(nèi)存中刪除。

源對象與上面的相同。然后:

family= null;

內(nèi)存中的圖片變成:


這個例子說明了可達性的概念是多么重要。

很明顯,John和Ann仍然鏈接在一起,都有傳入的引用。但這還不夠。

“family”對象已經(jīng)從根上斷開了鏈接,不再有對它的引用,因此下面的整個塊變得不可到達,并將被刪除。

內(nèi)部算法

基本的垃圾回收算法稱為“標(biāo)記-清除”,定期執(zhí)行以下“垃圾回收”步驟:

垃圾回收器獲取根并“標(biāo)記”(記住)它們。

然后它訪問并“標(biāo)記”所有來自它們的引用。

然后它訪問標(biāo)記的對象并標(biāo)記它們的引用。所有被訪問的對象都被記住,以便以后不再訪問同一個對象兩次。

以此類推,直到有未訪問的引用(可以從根訪問)為止。

除標(biāo)記的對象外,所有對象都被刪除。

例如,對象結(jié)構(gòu)如下:


我們可以清楚地看到右邊有一個“不可到達的塊”。現(xiàn)在讓我們看看“標(biāo)記并清除”垃圾回收器如何處理它。

第一步標(biāo)記根

然后標(biāo)記他們的引用

以及子孫代的引用:



現(xiàn)在進程中不能訪問的對象被認為是不可訪問的,將被刪除:


這就是垃圾收集的工作原理。JavaScript引擎應(yīng)用了許多優(yōu)化,使其運行得更快,并且不影響執(zhí)行。

一些優(yōu)化:

分代回收——對象分為兩組:“新對象”和“舊對象”。許多對象出現(xiàn),完成它們的工作并迅速結(jié) ,它們很快就會被清理干凈。那些活得足夠久的對象,會變“老”,并且很少接受檢查。

增量回收——如果有很多對象,并且我們試圖一次遍歷并標(biāo)記整個對象集,那么可能會花費一些時間,并在執(zhí)行中會有一定的延遲。因此,引擎試圖將垃圾回收分解為多個部分。然后,各個部分分別執(zhí)行。這需要額外的標(biāo)記來跟蹤變化,這樣有很多微小的延遲,而不是很大的延遲。

空閑時間收集——垃圾回收器只在 CPU 空閑時運行,以減少對執(zhí)行的可能影響。

面試怎么回答

1)問什么是垃圾

一般來說沒有被引用的對象就是垃圾,就是要被清除, 有個例外如果幾個對象引用形成一個環(huán),互相引用,但根訪問不到它們,這幾個對象也是垃圾,也要被清除。

2)如何檢垃圾

一種算法是標(biāo)記?標(biāo)記-清除?算法,還想說出不同的算法可以參考這里

更深入一些的講解?http://newhtml.net/v8-garbage...

還有一種牛逼的答法就是說看我的博客,當(dāng)然是要自己總結(jié)的博客。

參考文章

https://segmentfault.com/a/1190000018605776

http://www.itdecent.cn/p/a8a04fd00c3c

https://www.cnblogs.com/shytong/p/4999213.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Javascript 是自動進行內(nèi)存管理的,而且對于開發(fā)人員是不可見的。我們創(chuàng)建的基本類型,對象,函數(shù)……都需要消...
    bestCindy閱讀 467評論 0 0
  • 什么是垃圾回收機制? JS的垃圾回收機制是為了以防內(nèi)存泄漏,內(nèi)存泄漏的含義就是當(dāng)已經(jīng)不需要某塊內(nèi)存時這塊內(nèi)存還存在...
    令狐張豪閱讀 3,068評論 1 0
  • JavaScript中的垃圾回收是自動進行的,在平常開發(fā)中我們可能并不在意,但是深入理解JavaScript中的垃...
    Ying0223閱讀 331評論 0 0
  • 一、什么是閉包? 如果這個函數(shù)能夠訪問其他函數(shù)作用域中的變量,那么這個函數(shù)就叫做閉包。 換句話說,只要在一個函數(shù)中...
    Adonia汪閱讀 2,324評論 0 3
  • 夜鶯2517閱讀 128,148評論 1 9

友情鏈接更多精彩內(nèi)容