JS垃圾回收機制

1. 概述

JS的垃圾回收機制是爲了以防內存泄漏,內存泄漏的含義就是當已經不須要某塊內存時這塊內存還存在着,垃圾回收機制就是間歇的不按期的尋找到再也不使用的變量,並釋放掉它們所指向的內存。c++

C#、Java、JavaScript有自動垃圾回收機制,但c++和c就沒有垃圾回收機制,也許是由於垃圾回收機制必須由一種平臺來實現。在JS中,JS的執行環境會負責管理代碼執行過程當中使用的內存。瀏覽器

2. 變量的生命週期

當一個變量的生命週期結束以後它所指向的內存就應該被釋放。JS有兩種變量,全局變量和在函數中產生的局部變量。局部變量的生命週期在函數執行事後就結束了,此時即可將它引用的內存釋放(即垃圾回收),但全局變量生命週期會持續到瀏覽器關閉頁面。閉包

3. JS垃圾回收方式

JS執行環境中的垃圾回收器怎樣才能檢測哪塊內存能夠被回收有兩種方式:標記清除(mark and sweep)、引用計數(reference counting)。函數

3.1 標記清除(mark and sweep)

大部分瀏覽器以此方式進行垃圾回收,當變量進入執行環境(函數中聲明變量)的時候,垃圾回收器將其標記爲「進入環境」,當變量離開環境的時候(函數執行結束)將其標記爲「離開環境」,在離開環境以後還有的變量則是須要被刪除的變量。標記方式不定,能夠是某個特殊位的反轉或維護一個列表等。spa

垃圾收集器給內存中的全部變量都加上標記,而後去掉環境中的變量以及被環境中的變量引用的變量的標記。在此以後再被加上的標記的變量即爲須要回收的變量,由於環境中的變量已經沒法訪問到這些變量。設計

3.2 引用計數(reference counting)

這種方式經常會引發內存泄漏,低版本的IE使用這種方式。機制就是跟蹤一個值的引用次數,當聲明一個變量並將一個引用類型賦值給該變量時該值引用次數加1,當這個變量指向其餘一個時該值的引用次數便減一。當該值引用次數爲0時就會被回收。code

該方式會引發內存泄漏的緣由是它不能解決循環引用的問題:對象

function sample(){
    var a={};
    var b={};
    a.prop = b;
    b.prop = a;
}

這種狀況下每次調用sample()函數,a和b的引用計數都是2,會使這部份內存永遠不會被釋放,即內存泄漏。blog

低版本IE中有一部分對象並非原生JS對象。例如,其BOM和DOM中的對象就是使用C++以COM(Component Object Model)對象的形式實現的,而COM對象的垃圾收集機制採用的就是引用計數策略。生命週期

所以即便IE的js引擎是用的標記清除來實現的,可是js訪問COM對象如BOM,DOM仍是基於引用計數的策略的,也就是說只要在IE中設計到COM對象,也就會存在循環引用的問題。

當一個DOM元素和一個原生的js對象之間的循環引用時:

var ele = document.getElementById("eleId");
var obj = {};
obj.property = ele;
ele.property = obj;

添加 obj.property = null;ele.property = null;便可解除原生JS對象與DOM元素之間的鏈接。

當閉包中建立循環引用時:

window.onload = function outerFunction(){
    var obj= document.getElementById("eleId");
    obj.onclick = function innerfunction(){
        console.log(obj.id);
    }
}

上面這個代碼建立了一個做爲obj元素處理程序的閉包,而這個閉包則又建立了一個循環引用。obj引用了document.getElementById("element"),而document.getElementById("ele  Id")的onclick方法會引用包括 obj 之內的外部環境中的變量,所謂「外部環境」包括了包含函數的整個活動對象,因此必定會包括 obj(即便閉包沒有對 obj 進行直接的引用,例如上文程序中沒有 obj.id 出現,包含函數的活動對象(obj)中也依舊會保存一個引用)。

能夠改爲下面這個:

window.onload = function outerFunction(){
    var obj= document.getElementById("element");
    var id = obj.id;//將obj副本保存於變量id中,則不會使obj元素處理程序的閉包建立循環引用
    obj.onclick = function innerfunction(){
        console.log(id);
    }
    ele = null;//手動斷開 obj 對 document.getElemengById("element")的引用
}
相關文章
相關標籤/搜索