跳至主要內容

垃圾回收

狮子...大约 4 分钟面试JavaScript

垃圾回收策略

标记清除

标记清除:标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁。

缺点:内存碎片化;分配速度慢

在V8中通过 标记整理(Mark-Compact)算法 就可以有效地解决,它的标记阶段和标记清除算法没有什么不同, 只是标记结束后,标记整理算法会将活着的对象(即不需要清理的对象)向内存的一端移动,最后清理掉边界的内存

引用计数

引用计数法:它把对象是否不再需要简化定义为对象有没有其他对象引用到它。如果没有引用指向该对象(引用计数为 0),对象将被垃圾回收机制回收。

缺点:需要一个计数器,所占内存空间大;循环引用无法回收的现象

V8对GC的优化

掘金 - 「硬核JS」你真的了解垃圾回收机制吗open in new window

分布式垃圾回收

我们在上面也说过,现在大多数浏览器都是基于标记清除算法,V8 亦是,当然 V8 肯定也对其进行了一些优化加工处理,那接下来我们主要就来看 V8 中对垃圾回收机制的优化

​新生区 采用 并行回收老生区 采用 增量标记与惰性回收

新生代

新生代对象是通过一个名为 Scavenge 的算法进行垃圾回收。使用区和空闲区,开始时,标记使用区的活动对象,然后复制到空闲区并排序,接着清空使用区,最后交互使用区和空闲区。 当一个对象经过多次复制仍然存活或者复制的对象占用空间大于25%,会被移动到老生代。

老生代

它的整个流程采用的是基于 标记清除算法标记整理算法

并行回收

进行垃圾回收时就会阻塞 JavaScript 脚本的执行,需等待垃圾回收完毕后再恢复脚本执行,我们把这种行为叫做 全停顿

比如一次 GC 需要 60ms ,那我们的应用逻辑就得暂停 60ms ,假如一次 GC 的时间过长,对用户来说就可能造成页面卡顿等问题。

所谓并行,也就是同时的意思,它指的是垃圾回收器在主线程上执行的过程中,开启多个辅助线程,同时执行同样的回收工作。

新生代对象空间就采用并行策略,在执行垃圾回收的过程中,会启动了多个线程来负责新生代中的垃圾清理操作,这些线程同时将对象空间中的数据移动到空闲区域,这个过程中由于数据地址会发生改变,所以还需要同步更新引用这些对象的指针,此即并行回收

增量标记与惰性清理

  1. 增量就是将一次 GC 标记的过程,分成了很多小步,每执行完一小步就让应用逻辑执行一会儿,这样交替多次后完成一轮 GC 标记(如上图)
  2. 当增量标记完成后,假如当前的可用内存足以让我们快速的执行代码,其实我们是没必要立即清理内存的,可以将清理过程稍微延迟一下,让 JavaScript 脚本代码先执行,也无需一次性清理完所有非活动对象内存,可以按需逐一进行清理直到所有的非活动对象内存都清理完毕,后面再接着执行增量标记
  • 增量标记与惰性清理的优缺?

    • 优点:使主线程的停顿时间大大减少了,让用户与浏览器交互的过程变得更加流畅

    • 缺点:首先是并没有减少主线程的总暂停的时间,甚至会略微增加,其次由于写屏障机制的成本,增量标记可能会降低应用程序的吞吐量

并发回收

前面我们说并行回收依然会阻塞主线程,增量标记同样有增加了总暂停时间、降低应用程序吞吐量两个缺点,那么怎么才能在不阻塞主线程的情况下执行垃圾回收并且与增量相比更高效呢?

这就要说到并发回收了,它指的是主线程在执行 JavaScript 的过程中,辅助线程能够在后台完成执行垃圾回收的操作,辅助线程在执行垃圾回收的时候,主线程也可以自由执行而不会被挂起。

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.5