js垃圾回收机制

js垃圾回收机制

1. 什么是垃圾回收机制

垃圾回收(GC:garbage collection):我们的程序在工作中会产生一些垃圾,这些垃圾是程序中不用的或者是使用过的,在程序完成后,或者进行中,GC会负责进行回收垃圾,这个过程就是垃圾回收机制。

2. 垃圾是如何产生的

在我们开发中会进行变量声明(引用类型是保存在堆内存中,然后在栈内存中保存一个对堆内存中的实际对象的引用,因此js中对引用数据类型的操作都是对对象的引用的操作,而不是对对象本身进行操作;基本类型是保存在栈中,因此对基本类型的操作就是直接对其操作),这些变量都会在占用内存,这些分配过程不需要我们手动显示的给赋值,这个是V8引擎帮助我们分配的。但是很多时候我们只是进行了声明,未对其进行销毁,内存就没有得到释放,虽然V8引擎他自己有自己的一套回收机制,但是也存在他不能回收的,这部分内容就永远的存在了我们的内容中。这部分内容即是垃圾。

3. 为什么要进行垃圾回收

程序的运行需要内存,只要程序提出要求,操作系统或者运行时就必须提供内存,那么对于持续运行的服务进程,必须要及时释放内存,否则,内存占用越来越高,轻则影响系统性能,重则就会导致进程崩溃

4. 垃圾回收是怎样进行的

垃圾回收在我们前端来讲,大致就是找出程序不在需要的内存变量,然后释放它。目前存在几种垃圾回收的方法,各个浏览器也采用不同的回收机制。大致有如下几种:

标记清除法(Mark-Sweep)

此算法分为 标记 和 清除 两个阶段,标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁。大致过程如下:

  1. 在运行时将所有的变量都加上一个标记,他开始会将所有变量都默认为垃圾,标记为0
  2. 从根对象上开始遍历,把不是垃圾的节点标记为1
  3. 清理所有标记为0的垃圾,销毁并释放他们的内存空间。
  4. 将所有标记为1的节点修改为0,等待下一轮垃圾回收。
  • 优点

标记清除算法的优点只有一个,那就是实现比较简单,打标记也无非打与不打两种情况,这使得一位二进制位(0和1)就可以为其标记,非常简单

  • 缺点

标记清除算法有一个很大的缺点,就是在清除之后,剩余的对象内存位置是不变的,也会导致空闲内存空间是不连续的,出现了 内存碎片(如下图),并且由于剩余空闲内存不是一整块,它是由不同大小内存组成的内存列表,这就牵扯出了内存分配的问题

引用计数法(Reference Counting)

如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收,过程如下:

  1. 当声明了一个变量并且将一个引用类型赋值给该变量的时候这个值的引用次数就为 1
  2. 如果同一个值又被赋给另一个变量,那么引用数加 1
  3. 如果该变量的值被其他的值覆盖了,则引用次数减 1
  4. 当这个值的引用次数变为 0 的时候,说明没有变量在使用,这个值没法被访问了,回收空间,垃圾回收器会在运行的时候清理掉引用次数为 0 的值占用的内存
  • 优点

引用计数算法的优点我们对比标记清除来看就会清晰很多,首先引用计数在引用值为 0 时,也就是在变成垃圾的那一刻就会被回收,所以它可以立即回收垃圾

  • 缺点

引用计数的缺点想必大家也都很明朗了,首先它需要一个计数器,而此计数器需要占很大的位置,因为我们也不知道被引用数量的上限,还有就是无法解决循环引用无法回收的问题,这也是最严重的

  • 列子
var o = {
  a: {
    b:2
  }
};
// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 很显然,没有一个可以被垃圾收集


var o2 = o; // o2变量是第二个对“这个对象”的引用

o = 1;      // 现在,“这个对象”只有一个o2变量的引用了,“这个对象”的原始引用o已经没有

var oa = o2.a; // 引用“这个对象”的a属性
               // 现在,“这个对象”有两个引用了,一个是o2,一个是oa

o2 = "yo"; // 虽然最初的对象现在已经是零引用了,可以被垃圾回收了
           // 但是它的属性a的对象还在被oa引用,所以还不能回收

oa = null; // a属性的那个对象现在也是零引用了
           // 它可以被垃圾回收了

5. V8对GC的优化

  • 分代式垃圾回收

V8内部将堆内存分为新生代和老生代两块区域,采用不同的策略管理垃圾回收。新生代的对象为存活时间比较短的对象(1-8m),老生代对象为存活时间比较长或常驻内存的对象。

新生代对象通过一个Scavenge的算法进行垃圾回收,算法的主要内容是将堆内存一分为二,一个是处于使用状态的使用区,一个处于空闲状态的空闲区。新加入的对象都会被存放到使用区,但是使用区快被写满时,就会需要进行一次垃圾回收。在开始进行垃圾回收时,新生代垃圾回收器会对使用区的活动对象做标记,标记完成之后将使用区的活动对象复制进空闲区并进行排序,随后进入垃圾清理阶段,最后进行角色互换,把原来的使用区变成空闲区,把原来的空闲区边成使用区。当一个对象被多次复制后依然存在,它就会被系统认定为存活时间较长或常驻内存的对象,这种对象会被分配进老生代区域进行处理。

老生代采用的时上面说到的标记清除算法。上面也讲过标记清除算法存在一定的确定,而V8采用的是标记整理算法来进行优化。

分代式机制把一些新、小、存活时间短的对象作为新生代,采用一小块内存频率较高的快速清理,而一些大、老、存活时间长的对象作为老生代,使其很少接受检查,新老生代的回收机制及频率是不同的,可以说此机制的出现很大程度提高了垃圾回收机制的效率

posted @ 2021-09-07 14:13  Tutao1995  阅读(858)  评论(0编辑  收藏  举报