前端面試:談談 JS 垃圾回收機制

阿里雲最近在作活動,低至2折,有興趣能夠看看:
https://promotion.aliyun.com/...

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

最近看到一些面試的回顧,很多有被面試官問到談談JS 垃圾回收機制,說實話,面試官會問這個問題,說明他最近看到一些關於 JS 垃圾回收機制的相關的文章,爲了 B 格,就會順帶的問問。前端

想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!git

最近看到一篇講 JS 垃圾回收的國外文章,以爲講得明白,因此就翻譯過來了,但願對大家有所幫助。github

垃圾回收

JavaScript 中的內存管理是自動執行的,並且是不可見的。咱們建立基本類型、對象、函數……全部這些都須要內存。面試

當再也不須要某樣東西時會發生什麼? JavaScript 引擎是如何發現並清理它?算法

可達性

JavaScript 中內存管理的主要概念是可達性。segmentfault

簡單地說,「可達性」 值就是那些以某種方式可訪問或可用的值,它們被保證存儲在內存中。函數

1. 有一組基本的固有可達值,因爲顯而易見的緣由沒法刪除。例如:工具

  • 本地函數的局部變量和參數
  • 當前嵌套調用鏈上的其餘函數的變量和參數
  • 全局變量
  • 還有一些其餘的,內部的

這些值稱爲根。學習

2. 若是引用或引用鏈能夠從根訪問任何其餘值,則認爲該值是可訪問的。

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

JavaScript 引擎中有一個後臺進程稱爲垃圾回收器,它監視全部對象,並刪除那些不可訪問的對象。

一個簡單的例子

下面是最簡單的例子:

// user 具備對象的引用
let user = {
  name: "John"
};

圖片描述

這裏箭頭表示一個對象引用。全局變量「user」引用對象 {name:「John」} (爲了簡潔起見,咱們將其命名爲John)。John 的 「name」 屬性存儲一個基本類型,所以它被繪製在對象中。

若是 user 的值被覆蓋,則引用丟失:

user = null;

圖片描述

如今 John 變成不可達的狀態,沒有辦法訪問它,沒有對它的引用。垃圾回收器將丟棄 John 數據並釋放內存。

兩個引用

如今讓咱們假設咱們將引用從 user 複製到 admin:

// user具備對象的引用
let user = {
  name: "John"
};

let admin = user;

圖片描述

如今若是咱們作一樣的事情:

user = null;

該對象仍然能夠經過 admin 全局變量訪問,因此它在內存中。若是咱們也覆蓋admin,那麼它能夠被釋放。

相互關聯的對象

如今來看一個更復雜的例子, family 對象:

function marry (man, woman) {
  woman.husban = 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 如今是不可訪問的,並將從內存中刪除全部不可訪問的數據。

垃圾回收以後:

圖片描述

沒法訪問的數據塊

有可能整個相互鏈接的對象變得不可訪問並從內存中刪除。

源對象與上面的相同。而後:

family = null;

內存中的圖片變成:

圖片描述

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

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

「family」對象已經從根上斷開了連接,再也不有對它的引用,所以下面的整個塊變得不可到達,並將被刪除。

內部算法

基本的垃圾回收算法稱爲「標記-清除」,按期執行如下「垃圾回收」步驟:

  • 垃圾回收器獲取根並「標記」(記住)它們。
  • 而後它訪問並「標記」全部來自它們的引用。
  • 而後它訪問標記的對象並標記它們的引用。全部被訪問的對象都被記住,以便之後再也不訪問同一個對象兩次。
  • 以此類推,直到有未訪問的引用(能夠從根訪問)爲止。
  • 除標記的對象外,全部對象都被刪除。

例如,對象結構以下:

圖片描述

咱們能夠清楚地看到右邊有一個「不可到達的塊」。如今讓咱們看看「標記並清除」垃圾回收器如何處理它。

第一步標記根

圖片描述

而後標記他們的引用

圖片描述

以及子孫代的引用:

圖片描述

如今進程中不能訪問的對象被認爲是不可訪問的,將被刪除:

圖片描述

這就是垃圾收集的工做原理。JavaScript引擎應用了許多優化,使其運行得更快,而且不影響執行。

一些優化:

  • 分代回收——對象分爲兩組:「新對象」和「舊對象」。許多對象出現,完成它們的工做並迅速結 ,它們很快就會被清理乾淨。那些活得足夠久的對象,會變「老」,而且不多接受檢查。
  • 增量回收——若是有不少對象,而且咱們試圖一次遍歷並標記整個對象集,那麼可能會花費一些時間,並在執行中會有必定的延遲。所以,引擎試圖將垃圾回收分解爲多個部分。而後,各個部分分別執行。這須要額外的標記來跟蹤變化,這樣有不少微小的延遲,而不是很大的延遲。
  • 空閒時間收集——垃圾回收器只在 CPU 空閒時運行,以減小對執行的可能影響。

面試怎麼回答

1)問什麼是垃圾

通常來講沒有被引用的對象就是垃圾,就是要被清除, 有個例外若是幾個對象引用造成一個環,互相引用,但根訪問不到它們,這幾個對象也是垃圾,也要被清除。

2)如何檢垃圾

一種算法是標記 標記-清除 算法,還想說出不一樣的算法能夠參考這裏

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

還有一種牛逼的答法就是說看個人博客,固然是要本身總結的博客。

代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug

你的點贊是我持續分享好東西的動力,歡迎點贊!

交流

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

https://github.com/qq44924588...

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

clipboard.png

相關文章
相關標籤/搜索