JavaScript中的垃圾回收

JavaScript具有自動垃圾回收機制,也就是說,執行環境會負責管理代碼執行過程中使用的內存。
在這裏瀏覽器的垃圾回收策略通常有兩種:


一:標記清除
        JavaScript中最常用的垃圾收集方式是標記清除。當變量進入環境(例如,在函數中聲明一個變量)時,就將這個變量標記爲“進入環境”。從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,因爲只要執行流進入相應環境,就可能用到它們。而當變量離開環境時,則將其標記爲“離開環境”。這樣標記過後,垃圾回收器就會在下次運行的時候回收它們所佔的內存空間。

二:引用計數
    另一種不太常見的垃圾回收策略叫做引用計數,引用計數的含義是跟蹤記錄每個值被引用的次數。當聲明一個變量並將一個引用類型值賦給該變量時,則這個值的引用次數就是1.如果同一值又被賦給另外一個變量,則該值得引用次數加1.相反,如果包含對這個值得變量又取得了另外一個值,則這個值得引用次數減1。當這個值的引用次數變爲0時,則說明沒有辦法再訪問這個值了,因而就可以將其佔用的內存空間收回來。這樣,當垃圾回收器再運行,它也會釋放空間那些了。
    然而這種方式是存在弊端的。請看下面這個例子:
function problem(){
    var objectA=new Object();
    var objectB=new Object();

    objectA.somOtherObject=objectB;
    objectB.antherObject=objectA;
}
    在這個例子中,objectA和objectB通過各自的屬性相互作用;也就是說,這兩個對象的引用次數都是2.在採用標記清除策略的實現中,由於函數執行之後,這兩個對象都離開了作用域,因此這種引用的內存釋放並不存在問題。但採用引用計數策略的實現中,當函數執行完畢後,objectA和objectB還將繼續存在,因爲它們的引用次數永遠不會是0。當函數多次被調用的話,就會有大批量的內存不能夠回收。導致瀏覽器性能極度下降!然而引用計數的麻煩未就此終結。
    我們知道,IE中有一部分對象並不是原生JavaScript對象。例如,其BOM和DOM中的對象就是C++以COM(組件對象模型) 對象的形式實現的,而COM對象的垃圾回收機制採用的就是引用計數策略。因此,即使IE的JavaScript引擎是使用標記清除策略來實現的,但是JavaScript訪問的COM對象依然是基於引用計數策略的。換句話說,只要IE中涉及COM對象,就會存在循環引用的問題。(個人理解是:在處理COM對象的時候JavaScript原生對象處理也應該用的是引用計數策略。)請看下面這個例子:
var element = document.getElementById("some_element");
var myObject = new Object();
myObject.element = element;
element.someObject = myObject;
爲了避免上面的問題,你可以手動斷開原生JavaScript對象與DOM元素之間的連接。
myObject.element=null;
element.someObject=null;
    這樣,當垃圾回收器下次運行的時候就會刪除這些值,並且回收它們所佔用的內存。

爲了解決上述問題。IE9把BOM和DOM對象都轉換成了真正的JavaScript對象。這樣,就避免了兩種垃圾回收算法並存導致的問題,也消除了常見的內存泄漏現象。

性能問題:

垃圾收集器是週期性運行的,而且如果爲變量分配的內存數量很可觀,那麼回收工作量也是相當大的。在這種情況下,確定垃圾收集的時間間隔是一個非常重要的問題。說到垃圾收器多長時間運行一次,不禁讓人聯想到 IE 因此而聲名狼藉的性能問題。IE 的垃圾收集器是根據內存分配量運行的,具體一點說就是 256 個變量、4096 個對象(或數組)字面量和數組元素(slot)或者 64KB 的字符串。達到上述任何一個臨界值,垃圾收集器就會運行。這種實現方式的問題在於,如果一個腳本中包含那麼多變量,那麼該腳本很可能會在其生命週期中一直保有那麼多的變量。而這樣一來,垃圾收集器就不得不頻繁地運行。結果,由此引發的嚴重性能問題促使 IE7 重寫了其垃圾收集例程。隨着 IE7 的發佈,其 JavaScript 引擎的垃圾收集例程改變了工作方式:觸發垃圾收集的變量分配、字面量和(或)數組元素的臨界值被調整爲動態修正。IE7 中的各項臨界值在初始時與 IE6 相等。如果垃圾收集例程回收的內存分配量低於 15%,則變量、字面量和(或)數組元素的臨界值就會加倍。如果
例程回收了 85%的內存分配量,則將各種臨界值重置回默認值。這一看似簡單的調整,極大地提升了 IE在運行包含大量 JavaScript 的頁面時的性能。

事實上,在有的瀏覽器中可以觸發垃圾收集過程,但我們不建議讀者這樣做。在IE 中,調用 window.CollectGarbage() 方法會立即執行垃圾收集。在 Opera 7 及更高版本中,調用 window.opera.collect() 也會啓動垃圾收集例程。

管理內存:

使用具備垃圾收集機制的語言編寫程序,開發人員一般不必操心內存管理的問題。但是,JavaScript在進行內存管理及垃圾收集時面臨的問題還是有點與衆不同。其中最主要的一個問題,就是分配給 Web瀏覽器的可用內存數量通常要比分配給桌面應用程序的少。這樣做的目的主要是出於安全方面的考慮,目的是防止運行 JavaScript 的網頁耗盡全部系統內存而導致系統崩潰。內存限制問題不僅會影響給變量分配內存,同時還會影響調用棧以及在一個線程中能夠同時執行的語句數量。因此,確保佔用最少的內存可以讓頁面獲得更好的性能。而優化內存佔用的最佳方式,就是爲執行中的代碼只保存必要的數據。一旦數據不再有用,最好通過將其值設置爲 null 來釋放其引用——這個做法叫做解除引用(dereferencing) 。這個還是有必要的。這一做法適用於大多數全局變量和全局對象的屬性。局部變量會在它們離開執行環境時自動被解除引用

不過,解除一個值的引用並不意味着自動回收該值所佔用的內存。解除引用的真正作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章