[譯] 經過垃圾回收機制理解 JavaScript 內存管理

照片來自 Unsplash 上的 Dlanor Sjavascript

內存管理的主要目標是在須要的時候爲系統動態地分配內存,而後釋放那些再也不使用的對象的內存。像 C 和 C++ 這樣的語言有基本的內存分配函數,如 malloc(),而一些高級語言計算機體系結構(如 JavaScript)包含垃圾回收器來完成這項工做。它跟蹤內存分配並識別這些分配的內存是否再也不使用,若是是就自動釋放。可是這種算法不能徹底決定內存是否仍被須要。所以,對於程序員來講,理解並決定一段特定的代碼是否須要內存是很是重要的。讓咱們瞭解一下 JavaScript 中的垃圾收集是如何工做的:前端

垃圾回收

JavaScript 引擎的垃圾回收器基本上是尋找內存中被刪除的沒法訪問的對象。這裏我想解釋兩種垃圾回收算法,以下所示:java

  • 引用計數垃圾回收
  • 標記清除算法

引用計數垃圾回收

這是一個簡單的垃圾回收算法。這個算法尋找那些沒有被引用的對象。若是一個對象沒有被引用,那麼該對象就能夠被垃圾回收。android

var obj1 = {
    property1: {
         subproperty1: 20
     }
};
複製代碼

如上例所示,讓咱們建立一個對象以理解這個算法。這裏 obj1 引用了一個對象,其中的 property1 屬性也引用了一個對象。因爲 obj1 具備對象的引用,所以這個對象不會被垃圾回收。ios

var obj2 = obj1;

obj1 = "some random text"
複製代碼

如今,obj2 也引用了被 obj1 引用的同一個對象,但後來 obj1 被更新爲了 "some random text",這致使 obj2 具備對該對象的惟一引用。git

var obj_property1 = obj2.property1;
複製代碼

如今 obj_property1 指向 obj2.property1,它引用了一個對象。所以該對象有兩個引用,以下所示:程序員

  1. 做爲 obj2 的屬性
  2. 在變量 obj_property1
obj2 = "some random text"
複製代碼

經過更新爲 "some random text" 來取消 obj2 對對象的引用。所以,以前的這個對象彷佛沒有被引用了,因此它能夠被垃圾回收。可是,因爲 obj_property1 具備 obj2.property1 的引用,所以它不會被垃圾回收。github

obj_property1 = null;
複製代碼

當咱們把 obj_property1 引用刪除,如今最初 obj1 指向的對象沒有引用了。因此如今它能夠被垃圾回收。算法

這個算法在哪裏失敗了?

function example() {
     var obj1 = {
         property1 : {
              subproperty1: 20
         }
     };
     var obj2 = obj1.property1;
     obj2.property1 = obj1;
     return 'some random text'
}

example();
複製代碼

這裏引用計數算法在函數調用以後不會從內存中刪除 obj1obj2 ,由於兩個對象都是相互引用的。後端


標記清除算法

這個算法查找從根開始沒法訪問的對象,這個根是 JavaScript 的全局對象。該算法克服了引用計數算法的侷限性。沒有引用的對象是不可訪問的,可是反過來就不必定了。

var obj1 = {
     property1: 35
}
複製代碼

如上所示,咱們能夠看到建立的對象 obj1 如何從 ROOT 中訪問到的。

obj1 = null
複製代碼

如今,當咱們將 obj1 的值設置爲 null 時,該對象從根開始沒法被訪問,所以它能夠被垃圾回收。

該算法從根開始,遍歷全部其餘對象,同時標記它們。它進一步遍歷被遍歷的對象並標記它們。這個過程將被重複直到全部已被遍歷的節點沒有任何子節點和可遍歷的路徑。如今垃圾回收器會忽略全部可訪問對象,由於它們在遍歷時被標記。所以,全部未標記的對象顯然都是從根節點開始沒法訪問的,這意味着它們能夠被垃圾回收,稍後經過刪除這些對象釋放內存。讓咱們經過下面的例子來試着理解一下:

如上所示,這就是對象結構的樣子。咱們能夠注意到沒法從根開始訪問的對象,可是讓咱們嘗試瞭解在這種狀況下標記清除算法是如何工做的。

算法從根開始標記遍歷到的對象。上面的圖片中,咱們能夠注意到在對象上標記的綠色圓圈。這樣它就能夠將對象標識爲可從根開始能夠訪問到。

未被標記的對象是沒法從根開始被訪問到的。所以它們能夠被垃圾回收。

侷限性

對象必須顯式地設置爲不可訪問。

自 2012 年以來,JavaScript 引擎已經使用此算法來代替引用計數垃圾回收。

謝謝閱讀。


進一步閱讀:

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索