js垃圾回收机制

236 阅读5分钟

1.js类型

  • 在使用之前就需要确认其变量数据类型的称为静态语言,反之,称为动态语言
  • 支持隐式类型转换的语言称为弱类型语言,不支持隐式类型转换的语言称为强类型语言 那么:JavaScript是一种弱类型的、动态的语言。这意味着
  • 弱类型,不需要告诉JavaScript引擎这个或那个变量是什么数据类型,JavaScript引擎在运行代码的时候自己会计算出来。
  • 动态,意味着可以使用一个变量保存不同类型的数据。

2.js的内存空间

  • 代码空间
  • 栈空间,主要存放执行上下文,其中,变量环境中的基本类型直接存入栈,而引用数据类型存至堆空间
  • 堆空间,主要存放调用过程中的引用类型 要区分堆空间和栈空间的原因: JavaScript引擎需要用栈来维护程序执行期间的上下文状态,如果栈空间过大,所有数据都存放在栈空间里,会影响上下文切换的效率,进而影响整个程序的执行效率。比如某个函数执行结束了,JavaScript引擎离开当前的执行上下文,只需要将指针下移到上个执行上下文的地址就可以了,该函数执行上下文栈空间全部收回。

3.垃圾回收

(1)有些数据被使用之后,可能就不再需要了,我们把这种数据称为垃圾数据。如果这些垃圾数据一直保存在内存中,那么内存会越用越多,所以我们需要对这些垃圾数据进行回收,以释放有限的内存空间。
垃圾的产生一般主要有:

  • 未使用的全局变量
  • 缓存,一般缓存都需要通过键值的方式存放至堆空间的老生代空间
  • 作用域未释放,比如闭包
  • 队列消费未及时,比如node中的错误日志的入库
  • 未清除的无用定时器
  • 节点上监听的事件 垃圾的挥手方式一般有:
    • 手动回收
    • 自动回收

(2)手动回收的划分:

  • 未使用的全局变量可以为其赋值undifined或者null,也可通过delete方式删除
  • 增加缓存数量的限制,如果超出则模拟队列的方式直接清除首先入缓存的值
  • 不滥用闭包
  • 队列执行过程中,增加报警机制或者超出拒绝入库的策略
  • 在页面路由切换的过程中通过clearTimeout或者clearInterval清除定时器,也可直接赋值null
  • 路由切换或者组件销毁过程中,可以直接通过removeEventListener移除具名事件

(3)自动回收的划分:

  • 栈空间的释放 调用栈在执行过程中会有一个记录当前执行状态的指针(称为ESP),指向调用栈的执行上下文,表示当前正在执行的函数。接着,当某函数执行后,JavaScript会将ESP下移到下一个函数,这个下移过程就是销毁上一个函数执行上下文的过程。
  • 堆空间的释放 要回收堆中的垃圾数据,就需要用到 JavaScript 中的垃圾回收器了。

(4)堆空间的划分

image.png
其中,V8中的内存在默认条件下,如果一直分配内存,在64位系统和32位系统下会分别只能使用约为1.4GB和0.7GB。而新生代在64位系统和32位系统上分别为16MB和8MB,仅仅占其中的一小部分。

  • 新生代,新生代中存放的是生存时间短的对象,由副垃圾回收器回收 新生代用Scavenge算法来处理,就是把新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域。新加入的对象会被存放到对象区域,当对象区域快被写满的时候,就执行一次垃圾清理操作。副垃圾回收器会把存活的对象复制到空闲的区域中,并有序的排列起来,复制过程,也相当于内存整理操作。

完成复制后,对象区域与空闲区域进行角色翻转,角色翻转操作还能让新生代中的这两块区域无限重复使用下去。

也正是因为新生区的空间不大,所以很容易被存活的对象装满整个区域。为了解决这个问题,JavaScript 引擎采用了对象晋升策略,也就是经过两次垃圾回收依然还存活的对象,会被移动到老生区中。

  • 老生代,老生代中存放的生存时间久的对象,由主垃圾回收器回收 标记-整理(Mark-Compact)标记 - 清除(Mark-Sweep) 的引申。标记-清除是遍历调用栈,标记其中没有被引用的变量,然后将其标记,在接下来的垃圾清除过程中,清除掉标记的数据。而,标记-整理,不直接对可回收的对象进行清理,而是让其有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

4.全停顿

不过由于 JavaScript 是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。我们把这种行为叫做全停顿(Stop-The-World)

为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,我们把这个算法称为增量标记(Incremental Marking)算法

总结

如果面试被问到,可以从垃圾产生的原因,垃圾分类,清理方式,手动清理,自动清理,栈空间释放(ESP),堆空间释放,新生代(切换对象区域和空闲区域),对象区域,空闲区域,老生代(标记整理,移向一端),全停顿,停顿优化等角度作答。