zoukankan      html  css  js  c++  java
  • 垃圾收集

    JavaScript基于引用计数规则自动收集垃圾。如果一个对象不再被任何一个“引用”引用,那么称此对象不可达。JavaScript垃圾回收机制会在接下来的某一个时刻(无法预知的某时刻)回收此对象。

    var name = "hello";
    name = name.toUpperCase();
    // 此时“hello”对象已没有引用可以到达,所以“hello”对象会在接下来的某时刻被回收

    对象之间引用可达性导致不能回收的情况如下所示:

    /**
     * 主人,具有多个宠物
     * @param {Type}  
     */
    function Host() {
        "use strict";
        this.pets = {};
    }
    /**
     * 收养宠物
     * @param {string} name
     * @param {Pet} pet
     */
    Host.prototype.addPet = function (name, pet) {
        "use strict";
        this.pets[name] = pet;
        pet.host = this;
    };
    /**
     * 因为某些原因放弃宠物
     * @param {string} name
     */
    Host.prototype.removePet = function (name) {
        "use strict";
        this.pets[name].remove();
        delete this.pets[name];
    };
    /**
     * 宠物
     */
    function Pet() {
        "use strict";
        this.host = null;
    }
    /**
     * 宠物忘记自己的主人
     */
    Pet.prototype.remove = function () {
        "use strict";
        this.host = null;
    };
    /**
     * 狗
     */
    function Dog() {
        "use strict";
    }
    Dog.prototype = new Pet();
    /**
     * 狗不会忘记自己的主人
     */
    Dog.prototype.remove = function () {
        "use strict";
    };
    /**
     * 猫
     */
    function Cat() {
        "use strict";
    }
    Cat.prototype = new Pet();
    现在让Host收集一只狗与一只猫
    
    var host = new Host();
    var dog = new Dog();
    // 收养宠物狗
    host.addPet("xiaoGou", dog);
    var cat = new Cat();
    // 收养宠物猫
    host.addPet("xiaoMao", cat);

    所以可知当前这个三对象在内存中的情况如下所示:

    0b2aa85471f1f4167743262a297acd8b

    从上图可以看出,在内存中Host对象的pets数组中的元素分别指向Dog与Cat对象,而Dog与Cat对象中的host属性都指向Host对象。所以当前三个对象都可以通过某个引用到达,所以此三个对象都不会被回收。

    如果某一个主人因为某此原因不能再收养宠物时

    // 放弃宠物狗
    host.removePet("xiaoGou");
    // 放弃宠物猫
    host.removePet("xiaoMao");
    host = null;
    cat = null;

    Host使用removePet方法来放弃宠物,并在此方法中调用了pet.remove方法,使宠物也忘记自己的主人。但是由于Dog对象重写了remove方法,所以Dog并没有忘记自己的主人,所以当前的内存情况如下所示:

    8613d779581a4900b5fc1dfa51040c0c

    中于没有把dog引用设置为null,所以通过dog引用还可以到达Dog对象,而又因为Dog对象的host属性还指向Host对象,所以Host对象也是可达的,所以Dog与Host对象都不能被回收。最后把dog引用也设置为null,这时因为Dog对象不可达,所以Dog与Host对象都会被回收。

    dog = null;
    

    由此可见对于“可达性”的判断是指某一个对象是否能从JavaScript执行环境中的某引用出发而引用到此对象。

    DOM节点与JavaScript对象之间引用导致不可回收的情况。

    function createElem() {
        "use strict";
        var elem = document.createElement("div"); // 动态创建一个Div
        elem.id = "div_id";
        document.body.appendChild(elem); // 把Div添加doby中
    }
    createElem();

    以上代码向body中动态创建了一个Div,并把Div添加到了DOM树中显示。尽管createElem方法执行完成之后,它作用域内的变量都已不可达,但是因为Div已被添加到了DOM树中,所以Div还存于内存中没有被回收。一至到Div被从DOM树中的删除,那么Div节点才会被回收。

    function deleteElem(id){
        var elem = document.getElementById(id);
        document.body.removeChild(elem);
    }
    deleteElem("div_id");

    DOM节点也可能与JavaScript对象之间形成循环引用导致不可回收的情况。

    function Button(text) {
        var button = document.createElement('input');
        button.type = "button";
        button.value = text;
        this.className = "Button";
        this.button = button; // Button对象的button属性指向input[type="button"]的DOM节点。
        button.self = this; // input[type="button"]的DOM节点的self属性指向了Button对象。
    }
    // 添加Button对象到某个位置
    Button.prototype.appendTo = function (parentElem) {
        parentElem.appendChild(this.button);
    }
    // 添加Button的单击事件
    Button.prototype.addClickEvent = function(func) {
        this.button.onclick = func;
    }
    // 移除
    Button.prototype.remove = function(parentElem) {
        parentElem.removeChild(this.button);
    }

    使用以上代码创建一个Button对象。

    // 创建Button对象
    var btn = new Button("show className");
    // 为Button添加一个单击事件
    btn.addClickEvent(function () {
        console.log( this.self.className ); // Button
    });
    var parentElem = document.getElementById("parent_id");
    // 把button添加到DOM树中
    btn.appendTo( parentElem );

    在创建了一个Button对象之后,再给它添加了一个单击事件,并把它添加到DOM树中。当前在内存中形成的情况如下图所示:

    ae95e3561e966b5ab4fa8fbefd7a02bc

    现在如果input[type="button"]已使用完成,把它从DOM树中删除。

    btn.remove( parentElem );
    btn = null;
    parentElem = null;

    以执行以上代码之后,btn到Button对象的引用已不可达。input[type="button"]节点也从DOM树中删除。但是Button对象与input[type="button"]节点还不能被回收。因为DOM节点与JavaScript对象处于浏览器的不同引擎中(DOM节点处于渲染引擎,JavaScript对象处于JavaScript引擎),它们之间的相互引用就形成了循环引用,所以此时的Button对象与input[type="button"]节点都不能回收。具体的内存情况如下所示:

    dd905fd0a05ccc52f38302034b322ad1

    所以要想回收Button对象与input[type="button"]节点,就要断开它们之间的引用。修改代码如下所示:

    // 移除
    Button.prototype.remove = function(parentElem) {
        this.button.self = null; // 断开input[type="button"]到Button对象的引用
        parentElem.removeChild(this.button);
        this.button = null; // 断开Button对象到input[type="button"]的引用
    }

    再调用remove方法,把input[type="button"]节点从DOM树中移除。

    btn.remove(parentElem);
    btn = null;
    parentElem = null;

    之后,内存的情况如下所示:

    30bbd8628faa9da41f6c7f468c0a9b69

    可以从上图看出Button对象与input[type="button"]节点之间不再有引用关系,所以这时Button对象与input[type="button"]节点都可以被回收。

    还可能由闭包引起的内存不能回收的情况。

    function outter() {
        "use strict";
        var obj = {
            name : "wanggang",
            age : 100
        };
        return function () {
            return obj;
        };
    }
    
    var func = outter();
    var obj = func();
    console.log(obj.name);
    obj.name = "yxf";

    当执行12行的outter方法时,它会返回函数。而这个函数再会引用着outter函数的中一个局部变量,所以当没有执行13行代码之前,虽然outter函数的生命周期已结束,但是outter的obj变量还存在于内存中,没有回收。只有当执行完13行的代码之后,outter函数中的obj变量才会被回收。

    (本文的部分例子来自于Ajax in Action)

  • 相关阅读:
    [服务器]Windows Server 2008 64位1核1G安装SQL Server2008
    [工作]离职了!好好休息一下
    [工作]IT连和IT恋产品已完成第一版,准备上线运营
    [SQL Server]储存过程中使用临时表循环操作数据
    [Swift]Xcode格式化代码快捷键
    [Swift]使用Alamofire传递参数时报错
    [工作]记录一下目前的工作
    [Swift]Swift图片显示方式设置,控件UIImageView的contentMode属性设置
    [Swift]创建桥接文件,Swift使用MJRefresh刷新插件
    我遇到了改变的机会
  • 原文地址:https://www.cnblogs.com/wangg-mail/p/4354736.html
Copyright © 2011-2022 走看看