對於開發者來講,JavaScript 的內存管理是自動的、無形的。咱們建立的原始值、對象、函數……這一切都會佔用內存。javascript
當咱們再也不須要某個東西時會發生什麼?JavaScript 引擎如何發現它並清理它?html
JavaScript 中主要的內存管理概念是 可達性。java
簡而言之,「可達」值是那些以某種方式可訪問或可用的值。它們必定是存儲在內存中的。react
這裏列出固有的可達值的基本集合,這些值明顯不能被釋放。算法
比方說:編程
這些值被稱做 根(roots)。微信
若是一個值能夠經過引用或引用鏈從根訪問任何其餘值,則認爲該值是可達的。編程語言
比方說,若是局部變量中有一個對象,而且該對象有一個屬性引用了另外一個對象,則該對象被認爲是可達的。並且它引用的內容也是可達的。下面是詳細的例子。函數
在 JavaScript 引擎中有一個被稱做 垃圾回收器 的東西在後臺執行。它監控着全部對象的狀態,並刪除掉那些已經不可達的。post
這裏是一個最簡單的例子:
// user 具備對這個對象的引用
let user = {
name: "John"
};
複製代碼
這裏的箭頭描述了一個對象引用。全局變量 "user"
引用了對象 {name:"John"}
(爲簡潔起見,咱們稱它爲 John)。John 的 "name"
屬性存儲一個原始值,因此它被寫在對象內部。
若是 user
的值被重寫了,這個引用就沒了:
user = null;
複製代碼
如今 John 變成不可達的了。由於沒有引用了,就不能訪問到它了。垃圾回收器會認爲它是垃圾數據並進行回收,而後釋放內存。
如今讓咱們想象下,咱們把 user
的引用複製給 admin
:
// user 具備對這個對象的引用
let user = {
name: "John"
};
let admin = user;
複製代碼
如今若是執行剛剛的那個操做:
user = null;
複製代碼
……而後對象仍然能夠被經過 admin
這個全局變量訪問到,因此對象還在內存中。若是咱們又重寫了 admin
,對象就會被刪除。
如今來看一個更復雜的例子。這是個家庭:
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"
對象已經再也不與根相連,沒有了外部對其的引用,因此它變成了一座「孤島」,而且將被從內存中刪除。
垃圾回收的基本算法被稱爲 "mark-and-sweep"。
按期執行如下「垃圾回收」步驟:
例如,使咱們的對象有以下的結構:
咱們能夠清楚地看到右側有一個「沒法到達的島嶼」。如今咱們來看看「標記和清除」垃圾收集器如何處理它。
第一步標記全部的根:
而後他們的引用被標記了:
……若是還有引用的話,繼續標記:
如今,沒法經過這個過程訪問到的對象被認爲是不可達的,而且會被刪除。
咱們還能夠將這個過程想象成從根溢出一個巨大的油漆桶,它流經全部引用並標記全部可到達的對象。而後移除未標記的。
這是垃圾收集工做的概念。JavaScript 引擎作了許多優化,使垃圾回收運行速度更快,而且不影響正常代碼運行。
一些優化建議:
還有其餘垃圾回收算法的優化和風格。儘管我想在這裏描述它們,但我必須打住了,由於不一樣的引擎會有不一樣的調整和技巧。並且,更重要的是,隨着引擎的發展,狀況會發生變化,因此在沒有真實需求的時候,「提早」學習這些內容是不值得的。固然,除非這是一個純粹的利益關係。我在下面給你提供了一些相關連接。
主要須要掌握的內容:
現代引擎實現了垃圾回收的高級算法。
《The Garbage Collection Handbook: The Art of Automatic Memory Management》(R. Jones 等人著)這本書涵蓋了其中一些內容。
若是你熟悉底層(low-level)編程,關於 V8 引擎垃圾回收器的更詳細信息請參閱文章 V8 之旅:垃圾回收。
V8 博客 還不時發佈關於內存管理變化的文章。固然,爲了學習垃圾收集,你最好經過學習 V8 引擎內部知識來進行準備,並閱讀一個名爲 Vyacheslav Egorov 的 V8 引擎工程師的博客。我之因此說 「V8」,由於網上關於它的文章最豐富的。對於其餘引擎,許多方法是類似的,但在垃圾收集上許多方面有所不一樣。
當你須要底層的優化時,對引擎有深刻了解將頗有幫助。在熟悉了這門編程語言以後,把熟悉引擎做爲下一步計劃是明智之選。
本文首發於微信公衆號「技術漫談」,歡迎微信搜索關注,訂閱更多精彩內容。
現代 JavaScript 教程:開源的現代 JavaScript 從入門到進階的優質教程。React 官方文檔推薦,與 MDN 並列的 JavaScript 學習教程。
在線免費閱讀:zh.javascript.info
掃描下方二維碼,關注微信公衆號「技術漫談」,訂閱更多精彩內容。