垃圾回收機制
垃圾收集器必須跟蹤哪個變量有用哪個變量沒用,對於不再有用的變量打上標記,以備將來收回其占用的內存,內存泄露和瀏覽器實現的垃圾回收機制息息相關, 而瀏覽器實現標識無用變量的策略主要有下兩個方法:
引用計數
跟蹤記錄每個值被引用的次數。當聲明一個變量並將引用類型的值賦給該變量時,則這個值的引用次數就是1。如果同一個值又被賦給另一個變量,則該值的引用次 數加1.相反,如果包含對這個值引用的變量又取得另外一個值,則這個值的引用次數減1.當這個值的引用次數變成0時,則說明沒有辦法訪問這個值了,因此就 可以將其占用的內存空間回收回來。
如: var a = {}; 對象{}的引用計數為1
b = a; 對象{}的引用計數為 1+1
a = null; 對象{}的引用計數為2-1
所以這時對象{}不會被回收;
IE 6, 7 對DOM對象進行引用計數回收, 這樣簡單的垃圾回收機制,非常容易出現循環引用問題導致內存不能被回收, 進行導致內存泄露等問題, 如:
demo1:
var btn = $("button");
btn.onclick = function(){
//當這里也可以訪問到btn,說明function內部存在btn的引用
};
demo2:
function a () {
var x = { };
var y = {};
x.a = y;
y.a = x;
}
a();
函數a執行完后,本來x, y對象都應該在垃圾回收階段被回收, 可是由於存在循環引用,也不能被回收。
標記清除(mark-and-sweep)
到2008年為止,IE,Firefox,Opera,Chrome和Safari的javascript實現使用的都是標記清除式的垃圾收集策略(或類似的策略),只不過垃圾收集的時間間隔互有不同。
標記清除的算法分為兩個階段,標記(mark)和清除(sweep). 第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,把未標記的對象清除
markFromRoots():
worklist <- empty
for each fld in Roots
ref <- *fld
if ref != null && isNotMarked(ref)
setMarked(ref)
add(worklist,ref)
mark()
mark():
while not isEmpty(worklist)
ref <- remove(worklist)
for each fld in Pointers(ref)
child <- *fld
if child != null && isNotMarked(child)
setMarked(child)
add(worklist,child)
sweep(start,end):
scan <- start
while scan < end
if isMarked(scan)
setUnMarked(scan)
else
free(scan)
scan <- nextObject(scan)
atomic collect():
markFromRoots()
sweep(HeapStart,HeapEnd)
