Js回收机制原理

存在意义

JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。
在JS中,JS的执行环境会负责管理代码执行过程中使用的内存。

可达性

JavaScript 中内存管理的主要概念是可达性。

  1. 有一组基本的固有可达值,由于显而易见的原因无法删除。例如:
  • 本地函数的局部变量和参数
  • 当前嵌套调用链上的其他函数的变量和参数
  • 全局变量
  • 还有一些其他的,内部的
  • 这些值称为根。
  1. 如果引用或引用链可以从根访问任何其他值,则认为该值是可访问的。

例如,如果局部变量中有对象,并且该对象具有引用另一个对象的属性,则该对象被视为可达性, 它引用的那些也是可以访问的,详细的例子如下。

JavaScript 引擎中有一个后台进程称为垃圾回收器,它监视所有对象,并删除那些不可访问的对象。
在这里插入图片描述
相互关联的对象
function marry (man, woman) {
woman.husban = man;
man.wife = woman;

return {
father: man,
mother: woman
}
}

let family = marry({
name: “John”
}, {
name: “Ann”
})
在这里插入图片描述

内部算法-引用计数(reference counting)

在内存管理环境中,对象 A 如果有访问对象 B 的权限,叫做对象 A 引用对象 B。引用计数的策略是将“对象是否不再需要”简化成“对象有没有其他对象引用到它”,如果没有对象引用这个对象,那么这个对象将会被回收。

let obj1 = { a: 1 }; // 一个对象(称之为 A)被创建,赋值给 obj1,A 的引用个数为 1 
let obj2 = obj1; // A 的引用个数变为 2

obj1 = 0; // A 的引用个数变为 1
obj2 = 0; // A 的引用个数变为 0,此时对象 A 就可以被垃圾回收了
但是引用计数有个最大的问题: 循环引用。

function func() {
    let obj1 = {};
    let obj2 = {};

    obj1.a = obj2; // obj1 引用 obj2
    obj2.a = obj1; // obj2 引用 obj1
}
创建了两个对象,并相互引用,形成了一个循环,被调用之后离开了函数作用域,
所以已经没有用了,可以被回收了,但是之间相互引用,所以不会被回收。

要解决循环引用的问题,最好是在不使用它们的时候手工将它们设为空。
上面的例子可以这么做:
obj1 = null;
obj2 = null;

内部算法-标记-清除算法

这个算法把“对象是否是垃圾”简化定义为“对象是否可以获得”,如果找不到就是垃圾,将被垃圾回收机制回收。
分为标记和清除两个阶段
标记阶段,垃圾回收器会从根对象开始遍历。每一个可以从根对象访问到的对象都会被添加一个标识,于是这个对象就被标识为可到达对象。
清除阶段,垃圾回收器会对堆内存从头到尾进行线性遍历,如果发现有对象没有被标识为可到达对象,那么就将此对象占用的内存回收,并且将原来标记为可到达对象的标识清除,以便进行下一次垃圾回收操作。
在这里插入图片描述
在这里插入图片描述
清除所有标记,执行下一轮操作(因为内存会动态的发生改变)

即使出现两个循环引用,也没有关系,从根开始找不到他们,没有标记上,后面也会被清除掉
在这里插入图片描述

内存泄漏

意外的全局变量

   
  function fn() {
    a = new Array(10000000)                            
    console.log(a)
  }
  fn()

当全局变量用于临时存储和处理大量信息时,需要多加小心。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。

没有及时清理的计时器或回调函数

  没有及时清理的计时器或回调函数
  var intervalId = setInterval(function () { //启动循环定时器后不清理
    console.log('----')
  }, 1000)

  // clearInterval(intervalId)

闭包

闭包 是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量

function assignHandler(){
  var element = document.getElementById("someElement");
  var id = element.id;
  element.onclick = function(){
      alert(id);
  };
  element = null;
}

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
这是因为IE浏览器早期是使用引用计数法,如果两个对象之间形成了循环引用,那么这两个对象都无法被回收,但循环引用造成的内存泄露在本质上不是闭包造成的。

减少回收的优化

1、对象object优化

为了最大限度的实现对象的重用,应该像避使用new语句一样避免使用{}来新建对象。 有一种方式能够保证对象(确保对象prototype上没有属性)的重复利用,那就是遍历此对象的所有属性,并逐个删除,最终将对象清理为一个空对象。

cr.wipe = function (obj) {
    for (var p in obj) {
         if (obj.hasOwnProperty(p))
            delete obj[p];
    }
};

可以使用cr.wipe(obj)方法清理对象,再为obj添加新的属性,就可以达到重复利用对象的目的。虽然通过清空一个对象来获取“新对象”的做法,比简单的通过{}来创建对象要耗时一些,但是在实时性要求很高的代码中,这一点短暂的时间消耗,将会有效的减少垃圾堆积,并且最终避免垃圾回收暂停

2、 数组array优化

清空数组的捷径: arr = [];这种方式又创建了一个新的空对象,并且将原来的数组对象变成了一小片内存垃圾!
arr.length = 0也能达到清空数组的目的,并且同时能实现数组重用,减少内存垃圾的产生。

3、方法function优化

function func() {
    return function() {};            //每次调用函数时都会创建一个新的对象:
}

方法一般都是在初始化的时候创建,并且此后很少在运行时进行动态内存分配。在setTimeout中使用以上形式会造成问题。
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值