js內存空間的那點事


由於js具有自動垃圾回收機制,導致接觸js后一直沒去關注js的內存分配及變量回收等原理,只是懵懂的了解用變量標記法(null)可以手動的去清除或是回收;是時候彌補這個大坑了...

垃圾回收兩種方法 一種是 標記清除法另外一種是計數清除法,下面都會提到;

先來回顧/了解下垃圾回收實現算法----Mark-and-sweep, 此算法實現步驟

  1. 垃圾回收器創建了一個“roots”列表。Roots 通常是代碼中全局變量的引用。JavaScript 中,“window” 對象是一個全局變量,被當作 root 。window 對象總是存在,因此垃圾回收器可以檢查它和它的所有子對象是否存在(即不是垃圾);

  2. 所有的 roots 被檢查和標記為激活(即不是垃圾)。所有的子對象也被遞歸地檢查。從 root 開始的所有對象如果是可達的,它就不被當作垃圾。

  3. 所有未被標記的內存會被當做垃圾,收集器現在可以釋放內存,歸還給操作系統了。

 可以簡單的理解為 垃圾回收器會從window開始遞歸的標記window下所有子對象,在能訪問到的子對象上邊做個標記 告訴收集器這個對象/屬性我罩着 你不能動.而那些沒被標記的就視為"垃圾", 收集器會將它們釋放掉, window只是roots之一 對於其他的對象來講也是一樣的 ;

JS變量類型分為兩種即 原始類型(Number, String, Boolean, null, undefined)與引用類型(Object, Function, Array);

    棧: 遵循着后進先出,先進后出的原則, 屬於一級緩存, 地位相當於cpu的寄存器, 由編譯器自動分配釋放, 讀寫快, 存儲的都是固定值(也就是原始類型);

    堆: 屬於二級緩存,由我們分配釋放, 要是沒有手動的釋放,在調用結束后可能由GC回收,其生命周期由虛擬機的垃圾回收算法來決定。存儲的是引用類型;

在Js中 是不許直接操作堆的, 所以棧與堆的區分不是那么嚴謹, 只要用到了堆, 必然也用到了棧;上一段代碼幫助理解:

var a = 1,

  b = {m:20};

看起來像這樣:

可以清晰的看到在棧內存中變量b 存的是一個地址(引用地址,這個地址存放的就是{m: 20}), 由此我們可以看到js是不能直接操作堆的,操作的是這個引用地址; 而這個地址存放於棧中, 這就好理解為什么js棧與堆區分的不是那么嚴謹了.如果我們改變b的值,那會發生些什么,好,把b 改成b = [1, 2, 3]:

{m: 20}成了孤兒了.沒人罩着他了,所以os會處理掉. 這里就是os的另一種回收條件了--計數清除.將一個引用類型的值賦值給一個變量,那么這個引用類型的值的引用次數就加1,相反,如果這個變量被賦值了其他值,這個引用類型的值的引用次數就減1,當引用次數為0時,就說明沒有辦法再訪問這個引用類型的值了,那么她所占的內存空間會被垃圾回收器給回收;

接下來深淺復制就好理解了. 淺復制復制的引用地址, b復制了a的引用地址 他倆只要有一個對地址內容進行改變別人都會收到. 就像是你吧自家鑰匙給了一朋友,他買了一個新電腦進來, 當你回家你也會發現這個電腦;深復制呢,沒錯你給你朋友的不在是一把鑰匙而是一個房子, 這樣他在那房子干點什么你完全不知道也不受影響.下面在看看執行環境與棧之間的關系,看一段代碼:

var a = 1;

function fn(){

    var b = 2;

    function fn1(){

        console.log(b);

    }

    fn1();

}

fn();

 

運行后大致如下:

先進后出,所以會先進入fn1的執行環境..當fn1執行完在進入fn..一直到全局的執行環境,全局執行上下文永遠處於棧底.截止到這類似下面這些題一看就會了..

var a = 1,

    b = a;   a = 2;   //b?

沒錯 b還是1; 因為1是number 位於棧內存, b=a 的時候會深復制過來;

var a = {n: 1},

      b = a;

      a.n = 2;

    //b.n?

 

對於這種一年級的問題我們先放一放, 考慮這段代碼會引發什么問題(不用考慮兼容性):

var obj = document.getElementById("button");// 假設可以獲取到這個id為 button的Dom元素;

obj.addEventListener("click",function(){

    obj.style.background = "#f00";

},false);

確實會引發內存泄漏的情況,  原因就是回調里邊使用obj 保留了對外部obj的引用,導致不能被回收.;

類似這樣的:

var num= 12;
setInterval(function() {
    var obj = document.getElementById('button');
    if(obj) {
        obj.innerHTML = num;
    }
}, 1000);

內存泄漏不是想避免就能避免的, 寫代碼有意識的防范固然很重要,也要學會使用調試工具進行探查.

對還有一點:

解除引用並不代表收回該值所占的內存,解除引用的真正作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其收回;好吧. 對於js內存空間也就這點事了..


免責聲明!

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



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