V8垃圾回收策略与GC算法

618 阅读4分钟

「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

内存

编写代码时,我们常说要防止内存泄漏,少用闭包,达到前端性能优化

那么内存是怎么回收的,这与浏览器的v8引擎息息相关,关键点在于垃圾回收机制

垃圾回收的机制,工作内容就是查找垃圾释放空间、回收空间,它的方式有多种,也就是我们常说的GC算法

GC算法

GC算法基础为3个,v8常用的GC算法不止3个,但是也是基础的升华,下文回收策略分解

  • 引用计数
  • 标记清除
  • 标记整理

引用计数 核心思想:设置引用数,判断当前引用数是否为0
引用计数器
引用关系改变时修改引用数字
引用数字为0时立即回收
优点:

  1. 发现垃圾时立即回收
  2. 最大限度减少程序暂停
    缺点:
  3. 无法回收循环引用的对象
  4. 时间开销大
// 缺点1
function fn(){
    const obj1 = {}
    const obj2 = {}
    obj1.name = obj2
    obj2.name = obj1
    return '循环引用'
}

标记清除 核心思想:分标记和清除两个阶段完成
遍历所有对象找到标记活动对象
遍历所有对象清除没有被标记对象
回收相应的空间
优点:1. 解决循环引用不能回收的问题
缺点:1. 会产生空间碎片化的问题,浪费空间

标记整理 标记整理 可以看作是 标记清除 的增强
标记阶段的操作和标记清除一致
清除阶段会执行整理,移动对象位置
优点:1. 减少碎片化空间
缺点:1. 不会立即回收垃圾对象

V8

  • v8是一款主流的JavaScript执行引擎
  • v8采用即时编译
  • v8内存设限 64位不超过1.5g,32位不超过800m(操作系统)

V8垃圾回收策略

核心:采用分代回收思想
整个回收过程v8常用的GC算法:

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

v8将内存一分为二,分为新生代、老生代

新生代对象回收实现 新生代指的是存活时间较短的对象
用于存储新生代对象的空间较小( 32M|16M )

  1. 回收过程采用复制算法+标记整理
  2. 新生代内存区分为2个等大的空间
  3. 使用空间为From,空闲空间为To
  4. 活动对象存储于From空间
  5. 标记整理后将活动对象拷贝至To
  6. From与To交换空间完成释放 回收细节说明:
  • 拷贝过程中可能出现晋升
  • 晋升就是将新生代对象移动至老生代
    晋升的2个触发条件:
  • 一轮GC还存活的新生代需要晋升
  • To空间的使用率超过25%

老生代对象回收实现 老生代对象就是指存活时间较长的对象
用于存储新生代对象的空间较大,64位操作系统1.4G,32操作系统700M
主要采用标记清除、标记整理、增量标记算法

  1. 首先使用标记清除完成垃圾空间的回收
  2. 采用标记整理进行空间优化(当新生代晋升时并且老生代空间不足时)
  3. 采用增量标记进行效率优化
    细节对比:
  • 新生代区域垃圾回收使用空间换时间(有一半是空闲的)
  • 老生代区域垃圾回收不适合复制算法

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

将整段垃圾回收操作拆分成多个小步,组合完成垃圾回收,这样可以让垃圾回收与主程序交替执行,这样时间消耗更合理,用户体验更好

总结

GC的目的是为了实现内存空间的良性循环,但作为良好程序员的我们应该节约使用内存,避免频繁的垃圾回收,因为GC工作时应用程序是停止的,频繁且过长的GC会导致应用假死,用户使用中感知应用卡顿!