JS垃圾回收机制

2021/03/03 Javascript 共 1098 字,约 4 分钟

垃圾回收机制

内存生命周期

垃圾回收其实就是高效利用内存的一个过程,那我们首先需要知道内存生命周期是什么:

  • 内存分配:变量、函数、对象等生命阶段,JS自动进行内存分配
  • 内存使用:变量、函数、对象的调用、使用阶段
  • 内存回收:执行上下文退出,无用变量内存回收阶段

引用计数法

顾名思义,一个变量每被引用一次,计数+1,当计数为0时,说明没有被使用到,则可以清除

存在的问题

当变量A被变量B引用,同时变量B又引用了变量A时,造成了循环引用问题,此时会发生内存泄漏。

如何解决呢?可以在声明的变量不再使用以后,随手赋值null,手动清除

标记清除法

当变量进入执行上下文时,打标记进入;当变量离开执行上下文时,打标记离开离开标记的变量会在下一次GC时进行清除

V8引擎GC方法

V8为了存储高效,将内存分为新生代老生代 新生代:

  • 存储常用或者临时变量,采用更快的扫描算法
  • 将空间划分为几个不同用途/等级块,对空间有浪费
  • 空间换时间,这样的效率更快

老生代:

  • 新生代的这种算法只适合临时的变量存储场景,对于老生代并不适合
  • 老生代采用了标记清除标记整理算法

新生代Scavenge算法

主要是依靠新生代里两块划分的区域from-spaceto-space

  • 标记活动对象和非活动对象
  • 复制from-space的活动对象到to-space
  • 释放from-space的非活动对象的内存
  • from-spaceto-space互换

那GC是如何知道当前对象是否还活动呢?通过可达性判断

  • 从初始根对象(window,global)指针开始,向下搜索子节点
  • 可以搜索到的变量则可达,打上标记
  • 所有没有被打上标记的变量,则不可达,可以被GC回收

新生代变量可能存在老生代晋升:

  • 新生代内存还分为两部分A和B
  • A存储新分配的变量,当经历过一次GC以后,该变量如果还存在,则放进B中
  • 再经过一次GC以后,如果B中的该变量还存在,则晋升到老生代

老生代标记清除和标记整理

标记清除:

  • 对老生代变量进行标记扫描,标记活动对象
  • 对老生代进行清除扫描,将不活动的未标记对象清除
  • 几番操作下来,老生代内存区域会存在一个内存碎片的问题,导致磁盘存储利用效率下降,所以需要标记整理

标记整理:

  • 将内存随便进行一端方向移动,进行整理

常见内存泄露

  • 全局变量
    • 可以在使用完毕以后,手动赋值null
  • 闭包
  • 未被清除的定时器
  • 未清除的DOM元素引用(直接删除真实DOM,导致变量引用未释放)
const el = {
  button: document.getElementById('button')
}
document.body.removeChild(el.button)

[1] 深入理解谷歌最强V8垃圾回收机制

[2] 深入浅出JS GC垃圾回收

Search

    Table of Contents