V8内存管理

530 阅读4分钟

V8内存管理

  • 程序运行时需要分配内存
  • 分为堆内存和栈内存

栈内存

  • 存放基本类型和引用类型的指针
  • 空间是连续的,增加删除只需要移动指针,速度快
  • 空间是有限的,满了会报错
  • 栈一般是在函数执行时创建的,函数执行结束,栈销毁

堆内存

  • 内存空间不连续,空间较大
  • 主要存储JS中引用类型 V8内存管理-内存模型.png

堆内存分类

新生代

  • 生命周期较短的对象

老生代

  • 存放生命周期较长的对象
  • 新生代的对象经过两个周期的垃圾回收后,如果数据还在新生代中,则将它们放到老生代里
  • 分为指针对象,和数据对象

Code Space

  • 用于存放JIT已编译的代码
  • 唯一有执行权限的内存

Large Object Space

  • 专门存大对象
  • 不会被垃圾回收

Map Space

  • 存放对象的Map信息,即隐藏类
  • 隐藏类是为了提升对象属性的访问速度
  • V8为每个对象创建一个隐藏类,记录了对象的布局,包括所有的属性和偏移量

垃圾回收

  • 垃圾:程序结束后,不再使用的数据

新生代

  • 新生代有两个区域,数据区域From和空闲区域To
  • 通过广度优先遍历,从根对象出发,把能遍历到的对象,从From复制到To区域,From和To角色互换
  • 优点:不会出现内存碎片,缺点:浪费空间
  • 新生代的GC比较频繁
  • 新生代的对象晋升到老生代的条件:
    • 经过一次GC还活着的对象
    • 对象复制到To区域时,To区域的空间达到一定的限制 V8内存管理-新生代.png

老生代

  • 老生代里的对象,有些是从新生代晋升过来的,有些是比较大的对象直接分配到老生代里的,空间大、存活时间长
  • 如果采用新生代的算法,浪费空间
  • 采用标记清除和标记整理
标记清除
  • 标记:垃圾回收之前,把所有对象设置成白色,从GC的根节点开始,通过深度优先遍历的方式,把遍历到的对象标记成黑色。黑色是或者的对象,白色是要清除的对象
  • 清除:清除掉白的的对象
  • 内存不连续,有内存碎片 V8内存管理-老生代-标记清除.png
标记整理
  • 为了解决标记清除带来的内存碎片的问题
  • 在整理过程中,把活着的对象向内存区的一端移动,移动完成直接清理边界外的内存
  • 效率低,不会产生内存碎片。10次标记清除伴随一次标记整理

V8内存管理-老生代-标记整理.png

优化

  • 在垃圾回收阶段,JS脚步需要暂停,全停顿(Stop the world)
  • 如果时间过长,会引起卡顿
  • 性能优化
    • 把大任务拆成小任务,分步执行
    • 把任务放在后台执行,不占用主线程
JavaScript执行 垃圾标记、垃圾清理、垃圾整理 JavaScript执行
------------                           -------------->

Parallel(并行)

  • 新生代的垃圾回收采用并行的策略,开启多个辅助线程进行垃圾回收
  • 并行执行的时,也是全停顿,主线程不能进行任何操作,只能等待辅助线程结束
      ---辅助线程-->
      ---辅助线程-->
      ---辅助线程-->
------            ------>

增量标记

  • 老生代的对象又多又大,采用增量标记的方法进行优化
  • 把标记分为多个阶段,每个阶段只标记一部分,和主线程的执行穿插进行
  • 为了支持增量标记,垃圾回收需要支持暂停和恢复,采用黑白灰的标记方式
    • 黑:这个节点被GC根节点引用到,该节点和子节点都被标记完成
    • 灰:这个节点被GC根节点引用到,但是子节点没有被标记完成,表示正在处理中
    • 白:未被引用到,需要清理
  • 如果有灰节点,下次从灰节点恢复标记

V8内存管理-老生代-增量标记.png

----开始标记---增量标记---增量标记---清理--整理--->

Write-barrier写屏障

  • 当黑色指向白色节点时,会触发写屏障,会把白色节点设置为灰色

惰性清理

  • 如果内存够用,先不清理,等JS代码执行完再清理

并发回收

  • 增量标记和惰性清理没有减少暂停的时间
  • 并发回收就是主线程在执行过程中,辅助线程在后台完成垃圾回收工作
  • 标记全部有辅助线程处理,清理由辅助线程和主线程配合完成
   ---辅助线程标记-->    ---清理整理-->
   ---辅助线程标记-->    ---清理整理-->
------执行JS-->------------清理整理-->----->

并发和并行

  • 都是同时执行多个任务
  • 并行:同一时刻多个进程在运行
  • 并发:进行上下文快速的切换

原文