美文网首页
浅谈JS垃圾回收机制(GC)

浅谈JS垃圾回收机制(GC)

作者: 小陈wx | 来源:发表于2022-04-28 14:40 被阅读0次

    1.什么是垃圾?什么是垃圾回收?

    • 谈到垃圾,我们就必须谈谈内存,所谓垃圾其实就是指的一些不再使用的(可以弃用的)内存占用

    • 程序在运行过程中需要占用设备的内存空间,而每个设备的内存大小很显然是有上限的,部分软件也会对内存分配有一定的限制(如chrome中,单tab页内存空间上限在32位系统上为512M,64位系统上为1.4GB),如果在内存使用上不加以管理,那么程序在不断运行期间产生的数据最终会填满整个内存空间,导致程序后续无法顺利运行。
      所以需要将无用的内存占用释放掉,以便于新的数据再次占用这些内存。这个内存的清理过程就是垃圾回收。

    • 看到一些其他文章的举例,把整个内存空间比喻为一个餐厅,每个餐桌就是一块内存空间。客人找餐桌吃饭就是占用内存,吃完了离开,但是此时餐桌上还有盘子没有收拾干净,这就好比是产生了内存垃圾,随后服务员过来清理餐桌,等同于垃圾回收,随后该餐桌又可以重新投入使用

    //执行下面代码会导致内存占用超过上限,可能引起页面崩溃
    function grow() {
       var x = []
       let str = new Array(100000).join('x');
       // 1亿个
       for (let i=0; i<100000000; i++) {
          x.push(str)
       }
    }
    document.getElementById('grow').addEventListener('click', grow);
    

    2.js中的数据如何使用内存?

    • js中数据分为两类:

      1. 基本数据类型(原始):Undefined、Null、Boolean、Number、String等

      2. 引用数据类型:Object(由基本数据类型构成的key:value结构的复杂数据)

    • 可以看到上述两类数据类型中,基本数据相对简单,引用数据相对复杂。而栈储存操作简单(入栈出栈),访问栈中变量速度也快,JS引擎在运行过程中会利用栈空间来维护执行上下文的状态,为了保持上下文切换的高效率,所以分配的栈空间不能太大,并且里面塞入的数据不能太多,那么就不能把全部数据都塞入栈空间。js引擎的做法是将基本数据和复杂数据的堆空间存储地址(引用)存放在栈空间中,并随着函数执行完毕销毁栈空间中函数执行上下文里的基本数据和引用数据的指向(出栈),这里值得一提的是,内部函数保持对外部函数中变量的引用形成了闭包,此时这些闭包变量会组成一个对象保存在堆空间中,并不会随着外部函数执行完毕而被销毁。

    //执行下面函数可以在控制台中查看到出现了一个Closure对象其中包含变量name属性
    function fn(){
      var name="abc";
      return ()=>{
          debugger;
          console.log(name)
      }
    }
    var innerFn=fn();
    innerFn();
    

    3.JS何进行垃圾回收?

    由上诉答案得知JS运行过程中数据会存入栈空间和堆空间中,所以垃圾回收也可以分为两个部分来讨论:

    1.栈内存垃圾回收:
    • 在栈空间中存放的是短效数据,所以垃圾回收会相对简单,函数执行时按先后顺序入栈,在当前函数执行完毕后,该函数执行上下文出栈,并销毁该上下文中的所有变量,其中原始类型的变量会被直接释放,而对象类型变量的值在栈内存中只是保存的该对象在堆内存中的引用,所以并没有销毁变量本身,只是销毁了栈内存中记录的引用关系,该对象真正的值仍然储存在堆内存中;
    2.堆内存垃圾回收:
    • 代际假说,什么是代际假说,个人理解为一种普遍规律,这个规律认为通常情况下:

      1.大部分对象存活的时间很短,比如函数内部声明的变量,块级作用域中的变量,当函数执行完毕,作用域中的变量就会被销毁。这些形式的对象刚刚被塞入堆空间中,立马就变成不被访问的废数据(垃圾)。

      2.没有立即被销毁的对象,往往存活的很久,比如全局的window、DOM、WebAPI等对象。

      简单理解为,程序运行过程中对象生命周期大多处于2个极端,不是很短就是很长,基于这个规律,JS使用了两个垃圾回收器来针对不同生命周期的垃圾进行处理。

    • JS垃圾回收器:
      1.主垃圾回收器-Major GC(garbage collection),主要负责老生代内存垃圾回收。老生代内存中存储的是存活时间较长且占用空间较大的一些对象。由于老生代内存占用变化相对较少,且分配区域大,为了优化性能,所以该回收器回收操作频率较低。
      2.副垃圾回收器-Minor GC,主要负责新生代内存垃圾回收。新生代内存中存储的是存活时间较短的且占用空间较小的一些对象。由于新生代内存占用变化频繁,且被分配的区域小,所以改回收期操作频率高用于快速腾出内存空间。

    • 垃圾回收器如何工作:
      早期ie浏览器采用引用计数回收,计数为0时回收,每当有引用关系,则计数+1,失去一个引用关系,则计数-1,但是当函数内部出现循环引用,则两个循环对象引用计数为2,该函数执行上下文从调用栈中弹出,两个对象引用计数为2-1=1,不为0,因此该对象内存不被释放导致内存泄漏,主动破坏循环引用可以解决这个问题(循环对象1 /循环对象2 = null),但是为了更优雅的回收,后来出现了标记清除,这也是现代浏览器普遍使用的方式。
      标记清除分为多个步骤来进行,而且在主副垃圾回收器中工作方式略有不同,不过他们的原理相同,核心要义就是判断内存中对象是否可达,通常从根对象开始对全部子对象进行递归遍历,可以访问到的对象都是可达的,被标记为活动对象,没有遍历到的对象为非活动对象(垃圾),需要进行回收

    • 主垃圾回收器工作步骤:
      1.标记阶段:
      从根对象开始遍历标记全部对象,可达对象为活动对象,不可达对象为非活动对象。
      2.清除阶段:
      GC维护一个freeList列表,将非活动对象的内存片段释放,并把该内存地址添加到一个freeList中,每次有新对象需要进堆内存空间时,则优先查看freeList中的成员是否有合适空间供新对象使用,有的话则优先复用这些空间;
      3.内存整理阶段(可选):
      整理内存区域,内存在经过垃圾回收之后,活动对象将内存块分割的很零碎,将活动对象复制到相同连续的内存区域中。

    • 副垃圾回收器工作步骤:
      1.标记阶段:
      从根对象开始遍历标记全部对象,可达对象为活动对象,不可达对象为非活动对象。
      2.复制阶段:
      新生代将内存分为 from space(Nursery) 和 to space (Intermediate)。当有新对象申请内存,会分配from space 区域中的地址,to space 区域为备用区域。进入复制阶段时会将上一步中的活动对象复制到to space区中,并对该对象进行标记
      3.切换阶段:
      完成复制以后,from space 和 to space 两个区域互换身份,在一下次垃圾回收周期后,被两次
      为活动对象的对象会被复制到老生代区域。

    4.GC执行时机?

    • 早期GC运行在主线程,与JS程序执行代码交替执行,在GC执行阶段阻塞JS代码执行,假如需要处理的对象比较多,可能需要比较长的时间周期完成一次GC动作,此时会引起程序卡顿,表现为用户输入卡顿,动画卡顿等。

    • 在后来,设计出了效率更高体验更好的的GC执行方案”Orinoco“,Orinoco是 Google 垃圾回收器(Garbage Collector)的项目代号。这个方案简单总结为,并行(Parallel)、增量标记(Incremental)、并发(Concurrent):

      1.并行:在主线程之外开几个辅助线程同时进行垃圾清理,同异步执行原理(事件循环),只在标记完成时通知主线程清理,大大减少阻塞时间。

      2.增量:整个垃圾回收分成多个小任务,与JS交替执行,并不减少GC处理时长,但是单次阻塞时间减少,减低用户感知。

      3.并发:并发是主线程专注执行JS, 开启辅助线程进行垃圾回收。这种方式没有了全停顿,完全解放主线程,实现了 Free Main Thread 的目标(这里个人不是很理解,有了并发还要前面的并行和增量干嘛?????)。

    5.了解了JS垃圾回收机制以后,我们能干嘛?

    总的来说,js有一套自己的GC机制,它能自动帮我们的程序处理内存垃圾,通常情况下我们创建的变量不用之后会被自动释放,少数情况下我们的一些操作会让GC机制无法帮我们自动完成清理,比如:变量挂载到全局根对象下,闭包函数未执行,引用dom节点操作后不清空,事件监听完成后不处理,计时器使用后不清除,写代码的过程中避免掉这些操作能减轻垃圾回收器的负担。

    相关文章

      网友评论

          本文标题:浅谈JS垃圾回收机制(GC)

          本文链接:https://www.haomeiwen.com/subject/eucdyrtx.html