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内存空间也就这点事了..

posted @ 2017-05-18 20:07  彼岸黎明  阅读(2783)  评论(1编辑  收藏  举报