垃圾收集策略
自動垃圾收集機制原理
執行環境會負責代碼執行過程中使用的內存。
比如var a = 1
,會在棧上占有內存
找出不再繼續使用的變量,然后釋放其占用的內存。
垃圾收集器會按照固定的時間間隔(或根據代碼執行中預定的收集時間設置時間間隔),周期性地執行釋放內存操作。
標記清除策略
javascript最常用的垃圾收集方式是標記清除
當變量進入環境,比如
function(){
var a2=111;
}
a2變量將會被標記為“進入環境”
當變量離開環境時,變量會被標記為“離開環境”
垃圾收集器在運行的時候,
會給“進入環境”并存儲在內存中的所有變量加上標記。
var a3=111;
當變量“離開環境”時,
function fn(){
var c = 111;
}
fn()
局部變量在函數執行之后,“離開環境”
環境中的變量和被變量引用的變量的標記都會被去掉。
這些變量將被刪除,
因為環境中的變量已經訪問不到這些變量,
最后完成內存清理工作,銷毀相應變量的內存。
到2008年瀏覽器都用這種標記清除式的垃圾收集策略。
引用計數策略
跟蹤記錄每個值被引用的次數,
當聲明一個變量并將一個引用類型值賦值給該變量時,
var a4 ;
object = new Object()
a4 = object
這個值object的引用次數就是1。
當同一個值由被賦給另一個變量,
var a5;
a5 = object;
該值object的引用次數又加1,引用次數變為2。
相反,
包含這個值object引用的變量a5又被賦予另一值object2,
object2 = new Object();
a5 = object2;
則該值object的引用次數減1,引用次數變為1。
值object2的引用次數加1,引用次數為1 。
當該值object的引用次數為0時,
object3 =new Object()
a4 = object3
則說明沒法再訪問該值了。
實際上并沒有把值new Object()賦予變量object,
上面只是為了更好地理解對該值的引用。
因為IE瀏覽器的一部分對象并不是原生javascript對象,
其BOM、DOM中的對象是使用COM(組件對象模型)實現的,
這部分對象使用引用計數策略,
而原生javascript對象使用標記策略(其它瀏覽器則都使用該策略)
引用計數策略的缺陷
function(){
var element =document.getElementById('box')
var myObject = new Object();
myObject.someElement = element;
element.someObject = myObject;
}
在標記清除策略中,
該函數執行之后,
這兩個變量都離開作用域,兩個變量的標記也都被清除,
因此兩個值所在內存都會被釋放。
但是,
在引用計數策略中,
當函數執行之后,
兩個值仍然被循環引用,引用次數是2,內存不會被釋放,
則該函數在被重復多次調用之后,
因此會導致大量內存被占用而無法回收。
上例中,
element是DOM元素對象,myObject是原生javascript對象
因為DOM元素對象會使用引用計數策略,
所以會導致內存無法回收。
解決方案
myObject.someElement = null;
element.someObject = null;
變量設置為Null,會切斷變量與引用的值的連接。
當垃圾收集器下次運行時,
會刪除這些值,并回收它們占用的內存。
注意
IE9解決了上述缺陷,它將DOM和DOM對象都轉換為了原生的javascript對象。
管理內存
瀏覽器分配到的內存比桌面應用程度少。
導致一個線程中能同時執行的語句數量減少。
優化內存的方式:解除引用
局部變量會在離開執行環境時,
自動被解除引用,釋放內存。
全局變量和全局對象的屬性,則需要手動解除引用,
否則要等垃圾收集器運行時間到時才會解除引用,
function createPerson(name){
var localPerson = new Object();
//局部變量
localPerson.name = name
return localPerson;
}
var globalPerson = createPerson('jack');
//全局變量
globalPerson = null;
//解除對全局的引用,釋放該值內存
當聲明createPerson函數時,
function createPerson(name){...}
引用類型(new Object())的值被局部變量localPerson引用,值存儲到內存,
局部變量localPerson進入環境并被標記。
當執行createPerson函數時,
createPerson('jack')
,
局部變量localPerson離開環境并清除標記,釋放存儲該值的內存。
當var globalPerson = createPerson('jack');
,即值賦值給全局變量時,
引用類型(new Object())的值被全局變量globalPerson引用,值存儲到內存,
全局變量globalPerson進入環境并被標記。
當globalPerson = null;
,
全局變量被解除引用,釋放內存。