认识V8引擎

  • V8是一款主流的JavaScript执行引擎
  • V8采用即时编译(把源码直接翻译成可以立刻执行的机器码)
  • V8内存设限(64位操作系统不超过1.5G,对于32位不超过800M)

V8垃圾回收策略

  • 采用分代回收的思想
  • 内存分为新生代、老生代存储区
  • 针对不同对象采用不同的GC算法

V8中常用的GC算法

  • 分代回收
  • 空间复制
  • 标记清除
  • 标记整理
  • 标记增量

V8如何回收新生代对象

首先来看看V8的内存分配示例图:

  • V8的内存空间一分为二
  • 小空间用于存储新生代对象(32M:64位 | 16M:32位)
  • 新生代指的是存活时间较短的对象(比如说函数作用域里的变量)

新生代对象回收实现

  • 回收过程采用复制算法+标记整理
  • 新生代内存区分位两个等大小空间
  • 使用空间为From,空闲空间位To
  • 活动对象存储于From空间
  • 标记整理后将活动对象拷贝至To
  • From空间清除,释放掉整个空间
  • From与To交换空间,使得To成为From空间,From空间成为新的To空间

这个过程中,只复制了活动的对象,而非活动的对象被忽略,从而实现了对新生代对象的回收。由于新生代对象的特性是短命,这种复制算法比较适合,因为大多数新生代对象很快就会变成垃圾。

回收细节说明

  • 拷贝过程中可能出现晋升
  • 晋升就是将新生代对象移动到老生代
  • 一轮GC还存活的新生代对象就需要晋升
  • 如果拷贝过程中,To空间的使用率超过25%,就要将这次的活动对象都移动老生代存储区存放
    这里要额外说明:To空间的使用率如果超过一定限制,在将来进行空间交换,变为使用状态时,新进来的对象空间就不够用了,这就是要有使用率限制的原因。

V8如何回收老生代对象

  • 在上面的图里,我们可以知道老生代对象是存放在右侧的老生代区域
  • 64位操作系统1.4G,32位操作系统700M
  • 老生代对象就是指存活时间较长的对象(比如说,全局对象下存放的变量,闭包的一些变量数据)

老生代对象回收实现

  • 主要采用标记清除,标记整理,增量标记算法
  • 首先使用标记清除完成垃圾空间回收
  • 采用标记整理进行空间优化(如果发现新生代的内容往老生代区域移动,并且老生代空间不足以存放新生代的移动对象,也就是晋升,这个情况就会使用标记整理来处理碎片化的空间)
  • 采用增量标记进行效率优化

细节对比

  • 新生代区域垃圾回收使用空间换时间。因为新生代区域的空间很小,采用了复制算法,每时每刻内部都有空闲区域。分出来的空间又会更小,针对于时间上的显著提升,空间上的浪费就显得不是那么重要了。
  • 老生代区域垃圾回收不适合复制算法,因为老生代的大小很大,这就代表了有一半的区域无法在复制算法的执行过程中被使用,这个空间的容量就有几百兆左右,另外,老生代区域中的内容是比较多的,所以,在复制的过程中,我们消耗的时间是比较多的。

标记增量如何优化垃圾回收

有一点要明白:当垃圾回收机制开始工作的时候,他是会阻塞JavaScript程序执行的

在垃圾回收机制开始工作的时候,JavaScript就不会再继续执行了,而是等待遍历对象,这就是空档期。

所谓的标记增量就是,把整个垃圾回收操作分解为多个小部分,来组合着完成整个回收,替代之前一口气做完的垃圾回收操作。

好处就是:可以实现垃圾回收和程序执行交替的完成。这样带来的时间消耗会更加的合理一点。

最后的清除操作是没法分解的,就要让JavaScript停止运行了。

整个V8的最大的垃圾回收,达到了1.5G的时候,采用非增量标记的垃圾回收机制时间也不超过1秒钟。所以这段的间断间隔是合理的,最大限度的把很长的停顿时间直接拆分成了更小段,这就会对用户的体验有更好的优化

最后修改:2024 年 03 月 20 日
收款不要了,给孩子补充点点赞数吧