在公司常常會聽到大牛們討論時說道內存泄露神馬的,往往都驚羨不已,最近精力主要用在了Web 開發上,讀了一下《JavaScript高級程序設計》(書名很唬人,實際做者寫的特別好,由淺入深)瞭解了一下JavaScript垃圾回收機制,對內存泄露有了必定的認識。html
和C#、Java同樣JavaScript有自動垃圾回收機制,也就是說執行環境會負責管理代碼執行過程當中使用的內存,在開發過程當中就無需考慮內存分配及無用內存的回收問題了。JavaScript垃圾回收的機制很簡單:找出再也不使用的變量,而後釋放掉其佔用的內存,可是這個過程不是時時的,由於其開銷比較大,因此垃圾回收器會按照固定的時間間隔週期性的執行。瀏覽器
有同窗看了上面就會問了,什麼叫再也不使用的變量?再也不使用的變量也就是生命週期結束的變量,固然只多是局部變量,全局變量的生命週期直至瀏覽器卸載頁面纔會結束。局部變量只在函數的執行過程當中存在,而在這個過程當中會爲局部變量在棧或堆上分配相應的空間,以存儲它們的值,而後再函數中使用這些變量,直至函數結束(閉包中因爲內部函數的緣由,外部函數並不能算是結束,瞭解閉包能夠看看 JavaScript做用域鏈,JavaScript 閉包到底是什麼)。閉包
一旦函數結束,局部變量就沒有存在必要了,能夠釋放它們佔用的內存。貓和很簡單的工做,爲何會有很大開銷呢?這僅僅是垃圾回收的冰山一角,就像剛剛提到的閉包,貌似函數結束了,其實尚未,垃圾回收器必須那個變量游泳,那個變量沒用,對於再也不有用的變量打上標記,以備未來回收。用於標記無用的策略有不少,常見的有兩種方式函數
這是JavaScript最多見的垃圾回收方式,當變量進入執行環境的時候,好比函數中聲明一個變量,垃圾回收器將其標記爲「進入環境」,當變量離開環境的時候(函數執行結束)將其標記爲「離開環境」。至於怎麼標記有不少種方式,好比特殊位的反轉、維護一個列表等,這些並不重要,重要的是使用什麼策略,原則上講不可以釋放進入環境的變量所佔的內存,它們隨時可能會被調用的到。spa
垃圾回收器會在運行的時候給存儲在內存中的全部變量加上標記,而後去掉環境中的變量以及被環境中變量所引用的變量(閉包),在這些完成以後仍存在標記的就是要刪除的變量了,由於環境中的變量已經沒法訪問到這些變量了,而後垃圾回收器相會這些帶有標記的變量機器所佔空間。設計
大部分瀏覽器都是使用這種方式進行垃圾回收,區別在於如何標記及垃圾回收間隔而已,只有低版本IE,不出所料,又是IE。。。code
在低版本IE中常常會出現內存泄露,不少時候就是由於其採用引用計數方式進行垃圾回收。引用計數的策略是跟蹤記錄每一個值被使用的次數,當聲明瞭一個變量並將一個引用類型賦值給該變量的時候這個值的引用次數就加1,若是該變量的值變成了另一個,則這個值得引用次數減1,當這個值的引用次數變爲0的時候,說明沒有變量在使用,這個值無法被訪問了,所以能夠將其佔用的空間回收,這樣垃圾回收器會在運行的時候清理掉引用次數爲0的值佔用的空間。htm
看起來也不錯的方式,爲何不多有瀏覽器採用,還會帶來內存泄露問題呢?主要是由於這種方式沒辦法解決循環引用問題。好比對象A有一個屬性指向對象B,而對象B也有有一個屬性指向對象A,這樣相互引用對象
function test(){ var a={}; var b={}; a.prop=b; b.prop=a; }
這樣a和b的引用次數都是2,即便在test()執行完成後,兩個對象都已經離開環境,在標記清除的策略下是沒有問題的,離開環境的就被清除,可是在引用計數策略下不行,由於這兩個對象的引用次數仍然是2,不會變成0,因此其佔用空間不會被清理,若是這個函數被屢次調用,這樣就會不斷地有空間不會被回收,形成內存泄露。blog
在IE中雖然JavaScript對象經過標記清除的方式進行垃圾回收,但BOM與DOM對象倒是經過引用計數回收垃圾的,也就是說只要涉及BOM及DOM就會出現循環引用問題。看上面的例子,有同窗回以爲太弱了,誰會作這樣無聊的事情,其實咱們是否是就在作
window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){}; };
這段代碼看起來沒什麼問題,可是obj引用了document.getElementById("element"),而document.getElementById("element")的onclick方法會引用外部環境中德變量,天然也包括obj,是否是很隱蔽啊。
最簡單的方式就是本身手工解除循環引用,好比剛纔的函數能夠這樣
window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){}; obj=null; };
垃圾回收器週期性運行,若是分配的內存很是多,那麼回收工做也會很艱鉅,肯定垃圾回收時間間隔就變成了一個值得思考的問題。IE6的垃圾回收是根據內存分配量運行的,當環境中存在256個變量、4096個對象、64k的字符串任意一種狀況的時候就會觸發垃圾回收器工做,看起來很科學,不用按一段時間就調用一次,有時候會不必,這樣按需調用不是很好嗎?可是若是環境中就是有這麼多變量等一直存在,如今腳本如此複雜,很正常,那麼結果就是垃圾回收器一直在工做,這樣瀏覽器就無法兒玩兒了。
微軟在IE7中作了調整,觸發條件再也不是固定的,而是動態修改的,初始值和IE6相同,若是垃圾回收器回收的內存分配量低於程序佔用內存的15%,說明大部份內存不可被回收,設的垃圾回收觸發條件過於敏感,這時候把臨街條件翻倍,若是回收的內存高於85%,說明大部份內存早就該清理了,這時候把觸發條件置回。這樣就使垃圾回收工做職能了不少。
同C# 、Java同樣咱們能夠手工調用垃圾回收程序,可是因爲其消耗大量資源,並且咱們手工調用的不會比瀏覽器判斷的準確,因此不推薦手工調用垃圾回收。