JS垃圾回收機制


1. 概述

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

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

2. 變量的生命周期

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

3. JS垃圾回收方式

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

3.1 標記清除(mark and sweep)

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

垃圾收集器給內存中的所有變量都加上標記,然后去掉環境中的變量以及被環境中的變量引用的變量的標記。在此之后再被加上的標記的變量即為需要回收的變量,因為環境中的變量已經無法訪問到這些變量。

3.2 引用計數(reference counting)

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

該方式會引起內存泄漏的原因是它不能解決循環引用的問題:

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

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

低版本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")的引用
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM