天天看點

JavaScript記憶體管理與垃圾回收

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management
           

記憶體生命周期

不管什麼程式語言,記憶體生命周期基本是一緻的:

配置設定你所需要的記憶體

使用配置設定到的記憶體(讀、寫)

不需要時将其釋放\歸還

所有語言第二部分都是明确的。第一和第三部分在底層語言中是明确的,但在像JavaScript這些進階語言中,大部分都是隐含的。

JavaScript 的記憶體配置設定

值的初始化

為了不讓程式員費心配置設定記憶體,JavaScript 在定義變量時就完成了記憶體配置設定。
           

通過函數調用配置設定記憶體

有些函數調用結果是配置設定對象記憶體
有些方法配置設定新變量或者新對象
           

使用值

使用值的過程實際上是對配置設定記憶體進行讀取與寫入的操作。讀取與寫入可能是寫入一個變量或者一個對象的屬性值,甚至傳遞函數的參數。

當記憶體不再需要使用時釋放

大多數記憶體管理的問題都在這個階段。在這裡最艱難的任務是找到“所配置設定的記憶體确實已經不再需要了”。它往往要求開發人員來确定在程式中哪一塊記憶體不再需要并且釋放它。

進階語言解釋器嵌入了“垃圾回收器”,它的主要工作是跟蹤記憶體的配置設定和使用,以便當配置設定的記憶體不再使用時,自動釋放它。這隻能是一個近似的過程,因為要知道是否仍然需要某塊記憶體是無法判定的(無法通過某種算法解決)。

垃圾回收

如上文所述自動尋找是否一些記憶體“不再需要”的問題是無法判定的。是以,垃圾回收實作隻能有限制的解決一般問題。本節将解釋必要的概念,了解主要的垃圾回收算法和它們的局限性。

引用概念解釋

垃圾回收算法主要依賴于引用的概念。在記憶體管理的環境中,一個對象如果有通路另一個對象的權限(隐式或者顯式),叫做一個對象引用另一個對象。例如,一個Javascript對象具有對它原型的引用(隐式引用)和對它屬性的引用(顯式引用)。

在這裡,“對象”的概念不僅特指 JavaScript 對象,還包括函數作用域(或者全局詞法作用域)。

引用計數垃圾收集(IE6,7)

這是最初級的垃圾收集算法。此算法把“對象是否不再需要”簡化定義為“對象有沒有其他對象引用到它”。如果沒有引用指向該對象(零引用),對象将被垃圾回收機制回收。

該方法容易導緻記憶體洩漏,如兩個對象的循環引用,會使他們至少引用一次,而不會被回收

标記-清除算法(進階浏覽器使用)

這個算法把“對象是否不再需要”簡化定義為“對象是否可以獲得”。

這個算法假定設定一個叫做根(root)的對象(在Javascript裡,根是全局對象)。垃圾回收器将定期從根開始,找所有從根開始引用的對象,然後找這些對象引用的對象……從根開始,垃圾回收器将找到所有可以獲得的對象和收集所有不能獲得的對象。

這個算法比前一個要好,因為“有零引用的對象”總是不可獲得的,但是相反卻不一定,參考“循環引用”。

從2012年起,所有現代浏覽器都使用了标記-清除垃圾回收算法。所有對JavaScript垃圾回收算法的改進都是基于标記-清除算法的改進,并沒有改進标記-清除算法本身和它對“對象是否不再需要”的簡化定義。

循環引用不再是問題了

在上面的示例中,函數調用傳回之後,兩個對象從全局對象出發無法擷取。是以,他們将會被垃圾回收器回收。第二個示例同樣,一旦 div 和其事件處理無法從根擷取到,他們将會被垃圾回收器回收。

限制: 那些無法從根對象查詢到的對象都将被清除

盡管這是一個限制,但實踐中我們很少會碰到類似的情況,是以開發者不太會去關心垃圾回收機制。

繼續閱讀