我突然想起來了還有個博客帳號,最近工做任務較少,因此間歇性躊躇滿志又更新一下-\_-javascript
垃圾回收(garbage-collection)是指對已分配的內存進行回收,當咱們在建立一些變量,函數,對象時,例如:java
var a = "Hello"; var getName = function (){ //函數體}
都須要分配內存,而當這些值再也不被使用的時候,js就須要在合適的時候將這部分的內存進行回收,這就是垃圾回收機制,對於一些大型應用程序來講,垃圾回收能夠有效提升性能。在js裏,執行垃圾回收是自動執行的,不對外提供任何接口,不過仍是有必要適當瞭解下它的原理。算法
瞭解完垃圾回收的概念之後,首要的問題就來了:對於已分配出去的內存,什麼狀況下能夠進行垃圾回收呢?瀏覽器
聯繫下實際生活:咱們會把什麼樣的東西送去回收呢,那固然是肯定之後根本用不到的東西。閉包
在js裏,也是同樣,對於再也沒法訪問到的值,咱們就要進行回收。函數
舉個簡單例子:post
var user = { name: 'Leo' }; // 第一步,建立一個對象並把內存地址賦值給user user = null; // 第二步,修改user的內存地址
以前在其餘文章已經說過,建立引用類型值的時候,賦值給變量的實質上是內存地址。執行上述代碼的第一步以後,能夠經過window.user
訪問到{name: 'Leo'}
對象,性能
可是在執行Leo = null
以後,這個變量對象其實已經沒法訪問到了。也就達到了能夠被回收的條件。優化
再看個稍微複雜的案例:spa
var user = { name: 'Leo', friend: { name: 'John' } }; // 接下來移除引用 user.friend = null;// 第一種狀況,修改user.friend的地址 user = null;// 第二種狀況
這個例子中比前面多增長了一個對象{name: 'John'}
, 移除引用先後對應的圖以下:
如今你們應該對什麼叫作沒法被訪問有一個大概的概念了。 那麼來個特殊一點的:
// '朋友圈'函數 讓傳入的兩我的成爲朋友,並返回這個小圈子 function circleOfFriends(user1, user2){ user1.friend = user2; user2.friend = user1; return { user1, user2 } } var circle = circleOfFriends({name: 'Leo'},{name: 'John'});
執行這個以後,關係圖以下:
window
能夠經過circle
訪問到circle
對象,circle
能夠經過user1
`user2屬性訪問到
{name: 'Leo'},{name: 'John'}`兩個對象, 因此此時這三個對象都仍是可訪問的。
若是接下來依次執行如下步驟:
circle.user1 = null; //此時若是要訪問{name: 'Leo'}對象 還能夠經過circle.user2.friend 來實現,因此它依然是能夠訪問到的
接着執行:
circle.user2.friend = null; // 此時{name: 'Leo'}被完全孤立,不再可訪問,它知足能夠被回收的條件
注意,從圖中能夠看到{name: 'Leo'}
還有引用其餘對象, 可是它自身是經過任何方式都沒法被訪問到的。因此此時它仍是屬於可回收的。
固然,若是咱們不執行上面的語句,而是直接執行:
circle = null;
此時,circle指向的對象,{name: 'Leo'}對象,{name: 'John'}對象,都將變得不可訪問,即便他們之間內部是有互相引用關係。
到這裏,咱們彷佛就能夠概括出,知足可被回收的條件:
從全局對象出發,只要不可以直接或者間接被訪問到的值,就知足可回收的條件;
真的僅僅是這樣嗎? 咱們彷佛還遺漏了一個重要的地方-- 執行環境。 曾幾什麼時候,咱們在介紹閉包的時候講過執行環境和做用域鏈, 很顯然的,當前執行環境和做用域鏈上的值 也屬於可訪問的,例如最簡單的函數例子:
var b = 2; function getA(){ var a = 1; return a ; } getA();
當運行到getA()
以前時,變量b
是可訪問的,可是變量a
是不可訪問的, 當執行getA
函數內部時, 變量a
就是能夠訪問的了,固然此時變量b
也能夠訪問。
因此咱們須要完善一下上面的結論:
從當前執行環境以及做用域鏈上可訪問的對象出發,任何能夠被訪問的對象,都算是可訪問對象。
固然 若是認爲執行環境也是當前全局對象可訪問的對象之一,那同樣能夠用前面的簡化版結論,表述方式不必太較真,重點是要記住執行環境和做用域鏈這個要點。
(有印象的同窗不妨思考下好久以前說過的閉包,思考下閉包和垃圾回收機制的關係,後面有機會也會繼續補充說明)
其實上面的內容講完, 標記清除法的思路,也就基本清楚了。 標記清除法,顧名思義分爲2個步驟:
這裏介紹的是核心思路,具體實踐的時候有些地方是能夠優化的,例如第一步標記的過程,對於某些節點有可能被重複遍歷的,就像前面提到的'朋友圈'模型中,既能夠經過circle
訪問其中的某個對象, 也能夠經過某個user
的friend
屬性訪問到, 那麼爲了不重複遍歷,咱們就能夠另外用一種標記,來標識表示節點已經被遍歷過,詳細算法會在後續的系列文章介紹。
其實mdn還介紹了早期的另外一種回收算法,咱們也介紹提一下。
引用計數法顧名思義是就是先記下某個變量被引用的總次數,而後當被引用次數爲0時,就表示可回收。
這個思路咋一看和標記清除法是很類似的,可是實際上有個很明顯的區別,就是在前面互相引用的例子中:兩個user
其實都是不可得到的,可是因爲互相引用,它們的被引用次數並不爲0,那麼按照引用計數法,這兩個對象就不會被清除,這是有問題的, 因此從2012年起,全部的現代瀏覽器都是使用標記清除法。
文本主要介紹一下js中垃圾回收機制和兩種垃圾回收算法,但願你們看完後對此有個簡單的概念,其中比較重要的是辨析各類知足垃圾回收機制的情景,還有就是對象有被引用不等於可訪問(互相引用)關於垃圾回收機制其實還有蠻多的問題須要探索,例如:
會在此係列的後續文章上進一步更新說明。(應該會吧~)
慣例:若是內容有錯誤的地方歡迎指出(以爲看着不理解不舒服想吐槽也徹底沒問題);若是有幫助,歡迎點贊和收藏,轉載請徵得贊成後著明出處,若是有問題也歡迎私信交流,主頁有郵箱地址
而後還有一些想說的就是,很感謝你們的關注和一些私信,尤爲是看到一些讀者說看了文章之後確實有幫助的,會以爲很感動。 其實本身也滿慚愧的,每次更新都間好久,確實有點太懶了~, 之後會盡可能勤快一些。
順便再說下,RingCentral目前在杭州也設置了辦公點,並且能夠申請長期遠程辦公,幫你告別996,工做生活兩不誤,有興趣的同窗能夠私信或者發郵件給我,能夠免費幫忙內推~
http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection
https://javascript.info/garbage-collection
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory\_Management