聊一聊Chrome下V8引擎的垃圾回收机制

1. javaScript的内存管理

像C语言这样的底层语言一般都有底层的内存管理接口,比如 malloc()和free()。

相反,JavaScript是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。

释放的过程称为垃圾回收。这个“自动”是混乱的根源,并让JavaScript(和其他高级语言)开发者错误的感觉他们可以不关心内存管理。

无论什么语言内存的生命周期基本是一致的:
1. 分配你所需的内存
2. 使用分配到的内存读/写
3. 再不需要时将其回收/释放

所有语言第二部分都是明确的。第一和第三部分在底层语言中是明确的,但在像JavaScript这些高级语言中,大部分都是隐含的。

2.为什么需要垃圾回收

由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。

一句话:内存有限,需求无限

3.Chrome的垃圾回收算法

先说一下GC时,对象的大致流动过程: 对象刚被声明时会被存放在新生代区域nursery区域,对象经过第一次GC后存活下来的对象会被转移到intermediate区域中,对象经过第二次GC后,如果依然存活,那么对象就会被转移至老生代区域

浏览器是怎么辨别一个对象是否该删除还是该保留呢? 这里通过判断当前变量对根结点的可达性来作为依据。

总结:

对象生存时间短的,属于新生代。

对象生存时间久的属于老生代。

活动对象:根结点可达

非活对象:根结点对其不可达

下面开始详细介绍。

3.1 新生代垃圾收集器

在javascript中,任何对象的声明分配到的内存,将会先被放置在新生代中,而因为大部分对象在内存中存活的周期很短,所以需要一个效率非常高的算法。

  • 新生代中使用scavenge算法进行垃圾回收

scavenge 是一个复制算法。他会把当前A区域复制出来到B区域, 然后在B区域清理非活动对象并排序,结束后再进行赋值操作 把 A = B 一下

  • 新生代区域一般只有1-8MB的空间。

  • 对象晋升现象

每经过一次GC存活下来的对象就会晋升一次,直到转移到老生代区域下:nursery -> intermediate -> 老生代

3.2 老生代垃圾收集器

新生代空间中的对象在满足一定条件后会被转移到老生代区域。老生代使用 Mark-Sweep(标记清除) 和 Mark-Compact(标记整理) 算法进行垃圾回收工作。

3.2.1 mark-sweep 标记清除算法

Mark-Sweep处理时分为两阶段,标记阶段和清理阶段。在老生代中活动对象占大多数,所以Mark-Sweep在标记了活动对象和非活动对象之后,直接把非活动对象清除。

  • 标记阶段:对老生代进行第一次扫描,标记活动对象
  • 清理阶段:对老生代进行第二次扫描,清除未被标记的对象,即清理非活动对象

内存本身是一段连续的存储空间,在经过标记清除后,内存会变得不连续,被清除的对象遍布于各内存地址,产生很多内存碎片。所以引出了 mark-compact概念。

3.2.2 mark-compact

由于mark-sweep结束后会在老生代区域中产生很多的内存碎片,当需要分配一个很大的对象时就会出现问题,所以提出了mark-compact概念,在标记清除工作结束后对内存碎片进行整理,将所有的活动对象往一端移动,移动完成后,直接清理掉边界外的内存。

4. stop-the-world

避免内存资源竞争导致的不一致性问题。

由于垃圾回收是在js引擎中进行,而mark-compact在标记期间会移动对象,当活动对象很多时,这个过程就会非常的耗时,为了避免JavaScript应用逻辑和垃圾回收器的内存资源竞争导致的不一致性问题,垃圾回收器会将JavaScript应用暂停。如果老生代中的活动对象较多,垃圾回收器就会暂停主线程较长的时间,使得页面变得卡顿。

5. 优化 Orinoco

对垃圾回收进行优化,降低主线程的挂起时间

5.1 增量标记

穿插在主线程的各个阶段,对活动/非活动对象进行标记,只做标记动作

5.2 懒性清理

达到了阀值就会触发一次惰性清理,增量标记与惰性清理的出现,使得主线程的停顿时间大幅度减少,让用户与浏览器交互过程变得流畅了许多。

从实现机制上,由于每个小的增量标价之间执行了JavaScript代码,堆中的对象指针可能发生了变化,需要使用写屏障技术来记录这些引用关系的变化,所以也暴露出来增量标记的缺点:

  • 并没有减少主线程的总的暂停时间
  • 由于写屏障机制的成本,会应用增大开销
5.3 并发

并发式GC在标记时不需要挂起主线程,只有在个别时候需要短暂停下来让垃圾回收器做一些特殊的操作。存在指针同步问题。需要写屏障机制。

5.4 并行

一次GC可以交给多个线程同时进行,同样引入了写屏障机制。

6. V8当前垃圾回收机制

2011年,Chrome引入了增量标记。2018年Chrome64和node V10启动了并发标记。同时引入了多线程带来的同步问题。

6.1 副垃圾回收器

V8在新生代垃圾回收中,使用并行机制,在整理排序阶段,启用多个辅助线程,并行的进行整理。

6.2 主垃圾回收器

V8在老生代垃圾回收中,堆中内存超过某个阀值后会启用并发标记,辅助线程追踪对象的指针和引用。过程中如果对象发生了变更,写入屏障技术会对指针进行跟踪。当并发标记完成或内存达到极限时,主线程开始接手工作:根据根集进行check动作,check结束后进行GC。

课后作业:GC相关的名词列表:

新生代区域;
nursery;
intermediate;
老生代区域;
对象晋升;
mark-sweep 标记清除;
mark-compact 标记整理;
stop-the-world 全停顿;
优化orinoco;增量标记;
惰性清理;
并发concurrent;
并行parallel;
副垃圾收集器;
主垃圾收集器;
活动对象/非活动对象;
垃圾回收过程;

参考文献

深入理解Chrome V8垃圾回收机制

「译」Orinoco: V8的垃圾回收器

Trash talk: the Orinoco garbage collector

Chrome 浏览器垃圾回收机制与内存泄漏分析

JavaScript中的堆栈以及垃圾回收机制

超详细的node/v8/js垃圾回收机制

垃圾回收技术

4类 JavaScript 内存泄漏及如何避免

内存管理

Nodejs中的内存管理和V8垃圾回收机制

内存管理速成教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值