V8工作原理
栈空间和堆空间
-
JavaScript是一种弱类型(支持隐式类型转换)的、动态(运行过程中需要检查数据类型)的语言。
- JavaScript数据类型
- Boolean
- String
- Number
- Null
- Undefined
- Symbol
- BigInt
- Object
-
内存空间
- 代码空间
-
栈空间
- 原始类型的数据值都是直接保存在“栈”中,原始类型的赋值会完整复制变量值。
- 栈的空间一般不会设置太大,主要存放一些原始类型的小数据
-
堆空间
- 引用类型的值存放在“堆中”,引用类型的赋值是复制引用地址。
- 引用类型的数据占用空间都比较大,堆空间很大,可以存放很多大的数据。
垃圾回收
-
调用栈的数据回收
- JavaScript引擎会通过向下移动ESP(记录当前执行状态的指针)来销毁该函数保存在栈中的 上下文。
-
堆中数据回收
- JavaScript垃圾回收器
-
两个垃圾回收领域的术语
-
代际假说
- 特点1:大部分对象在内存中存在的时间都很短
- 特点2:不死的对象,会活得更久
- 分代假说
-
- V8中会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象;老生代中存放的生存时间久的对象。
- 副垃圾回收器,主要负责新生代的垃圾回收
-
主垃圾回收器,主要负责老生代的垃圾回收
- 垃圾回收器的工作流程
-
共同的执行流程
- 标记空间中活动对象和非活动对象
- 回收非活动对象的所占内存
- 内存整理
-
副垃圾回收器
- 负责新生代区的垃圾回收,把新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域(Scavenge算法)。
- 新加入的对象都会存放到对象区域,当内存快被写满时,就要执行一次垃圾清理工作。
- 在垃圾回收过程中,首先要对对象区域中的垃圾做标记。标记完成之后,就进入垃圾清理阶段,副垃圾回收器会把这些存活的对象复制到空闲区域中,并把这些对象有序的排列起来。
- 完成复制之后后,对象区域和空闲区域进行角色翻转。这种角色翻转的操作还能让新生代中这两块区域无限重复使用下去。
- 为了执行效率,一般新生代的空间会被设置的比较小
- 对象晋升策略,如果进行两次垃圾回收还存活的对象,会被移到老生区中。
-
主垃圾回收器
- 主垃圾回收器主要负责老生区中的垃圾回收。
- 老生区对象的两个对象:一是对象占用空间大,二是对象存活时间长。
- 主垃圾回收器是采用标记-清除(Mark-Sweep)算法进行垃圾回收的。
- 首先是标记阶段,从一组根元素开始,递归遍历这组根元素,遍历过程中,能够达到的成为活动对象,不能达到的元素可以判断为垃圾数据。
- 垃圾清除阶段,清除标记为垃圾标记的数据。
- 标记-清除(Mark-Sweep)算法会产生大量的不连续内存碎片,会导致大对象无法分配到足够的连续内存。需要标记-整理(Mark- Compact),标记过程与标记-清除的标记过程是一样的。而整理步骤是让活动对象向一端移动,然后清除掉端边界以外的内存
-
全停顿
- JavaScript是运行在主线程之上的,一段执行垃圾回收算法,都需要将JavaScript脚本停止下来,待垃圾回收完毕之后再恢复脚本执行。这种行为叫做全停顿。
- 在V8新生代的垃圾回收中,因其内存占用小,存活对象少,所以全停顿影响不大。
-
为了降低老生代的垃圾回收而造成的卡顿,V8将标记过程拆分成一个个的子标记过程,同时让垃圾回收标记与JavaScript应用逻辑交替执行,直到标记过程完成。这个算法成为增量算法(Incremental Marking)。使用增量标记算法,可以将垃圾回收任务拆成很多小的任务。这些小的任务执行时间较短,可以穿插在其他JavaScript任务中间执行。
- 思考:
- 堆中的经过垃圾回收整理之后,活动的对象内存地址改变了,内存变化如何更新到对应的执行上下文中?
此文章为4月Day8学习笔记,内容来源于极客时间《浏览器原理》,学习使我快乐,每天进步一点点💪💪
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net