第四章 作用域和内存问题 第三节垃圾收集

1、函数中局部变量的正常生命周期是怎样的?
2、垃圾收集方式是什么?
3、如何标记变量?
4、垃圾收集器的流程是什么?
5、引用计数是什么意思?
6、如何回收垃圾收集器占用的内存?
7、管理内存中的解除引用是什么?


1、函数中局部变量的正常生命周期是怎样的?
    局部变量只在函数执行的过程中存在,而在这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储他们的值,然后在函数中使用这些变量,直至函数执行结束。此时,局部变量就没有存在的必要了,因此可以释放他们的内存以供将来使用。

2、垃圾收集方式是什么?
    最常用的垃圾收集方式是标记清除,当变量进入环境时(在函数中声明一个变量),就将这个变量标记为"进入环境"。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只有执行流进入相应的环境,就可能会用到他们,而当变量离开环境时,则将其标记为"离开环境";

3、如何标记变量?
    可以通过翻转某个特殊的位来记录一个变量何时进入环境,或者使用一个"进入环境的"变量列表及一个"离开环境的"变量列表来跟踪哪个变量发生了变化,如何标记变量不重要,关键在于采取什么策略。

4、垃圾收集器的流程是什么?
    垃圾收集器在运行的时候会给存储在内存中的所有变量加上标记(可以使用任何标记方式),然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记,而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。5、引用计数是什么意思?
    引用计数是一种不太常见的垃圾收集策略,含义是跟踪记录每个值的引用次数,当声明了一个变量并将一个引用类型赋给该变量时,则这个值的引用次数就是1,如果同一个值又被赋给另一个对象,则该值的引用次数加1。相反,如果包含引用的变量又取得另外一个值,则这个值的引用次数减1.当次数为0时,则说明没有办法再发问这个值了。因而就可以将其占用的内存空间回收回来。这样就会释放引用次数为0所占用的内存。
    Netscape Navigator 3.0浏览器最早引用计数策略,遇到了一个严重的问题:循环引用,引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。
    如下:

    function problem(){
        var objA = new Object();
        var objB = new Object();

        objA.someOtherObject = objB;
        objB.anotherObject = objA;
    }

    其BOM和DOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾收集机制采用的就是引用计数策略,因此,即使IE的JavaScript引擎是使用标记清除策略来实现的,但JavaScript访问的COM对象依然是基于引用计数策略的。也就是说只要在IE中涉及COM对象,就会存在循环引用的问题。
    如下使用COM对象导致的循环引用问题:

    var element = document.getElementById("some_element");
    var myObject = new Object();
    myObject.element = element;
    element.someObject = myObject;

    以上在一个DOM元素(element)与一个原生JavaScript对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为element的属性指向element对象;而变量element也有一个属性名叫someObject回指myObject。由于存在这个循环引用,即使将例子中的DOM从页面中移除,它也永远不会被回收。

6、如何回收垃圾收集器占用的内存?    
    将变量设置为null意味着切断变量与它此前引用的值之间的连接,当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存,最好是在不使用它们的时候手工断开原生JavaScript对象与DOM元素之间的连接,
    如下:

    myObject.element = null;
    element.someObject = null;

7、管理内存中的解除引用是什么?
    需要确保占用最少的内存让页面获得更好的性能,而优化内存占用是最佳的方式,就是为执行中的代码只保存必要的数据,一旦数据不再有,最好通过将其值设置为null来释放其引用,这个做法叫解除引用。适用于大多数全局变量和全局对象的属性,局部变量会在它们离开执行环境时自动解除引用。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。
    如下:

    function createPerson(name) {
        var localPerson = new Object();
        localPerson.name = name;
        return localPerson;
    }

    var globalPerson = createPerson("Nicholas");

    //手工解除 globalPerson 引用
    globalPerson = null;

    以上变量globalPerson 取得了 createPerson()函数返回的值,在 createPerson() 函数内部,我们创建了一个对象并将其赋给局部变量 localPerson, 然后又为该对象添加了一个名为 name 的属性,最后,当调用这个函数时,localPerson 以函数值的形式返回并赋给全局变量 globalPerson。 由于 localPerson 在 createPerson() 函数执行完毕后就离开了其执行环境,因此无需我们显式地去为它解除引用。但对于全局变量 globalPerson 而言,则需要我们在不使用它的时候,手工解除引用。这就是上面最后一行代码的目的。

    小结部分:
    JavaScript 变量可以保存两种类型的值: 基本类型值和引用类型值;
     基本数据类型有5种:undefined、null、Boolean、number、string
     基本类型值和引用类型值具有以下特点:
         a: 基本类型值在内存中占固定大小的空间,因此保存在栈内存中;
         b: 从一个变量向另一个变量复制基本类型的值,会创建该值的副本;
         c: 引用类型的值是对象,保存在堆内存中;
         d: 从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象;
         e: 确定一个值是哪张类型可以使用 typeof 操作符,确定一个值是那种引用类型可以使用 instanceof 操作符
         f: 所有变量(包含基本类型和引用类型) 都存在一个执行环境(也称为作用域)当中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。

    执行环境总结:
        a: 执行环境有全局执行环境(也称全局环境)和函数执行环境之分;
        b: 每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用域链;
        c: 函数的局部环境不仅有权访问函数作用域中的变量,而且有权访问其包含(父)环境,乃至全局环境;
        d: 全局环境只能访问在全局环境中定义的变量和函数,而不能直接访问局部环境中的任何数据;
        e: 变量的执行环境有助于确定应该何时释放内存;

    JavaScript是一门具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题,垃圾收集器总结如下:
        a: 离开作用域的值将被自动标记为可回收,因此将在垃圾收集期间被删除
        b: "标记清除"是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存。
        c: 当代码中存在循环引用现象时,"引用计数"算法会导致问题
        d: 解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处,为了确保有效地回收内存,应该及时解除不在使用的全局对象,全局对象属性以及循环引用变量的引用。

转载于:https://my.oschina.net/guobao6/blog/3092570

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值