V8工作原理
栈空间和堆空间
JS 是一门弱类型、动态的语言
- 弱类型:意味着不需要告诉js引擎数据类型,它会自动判断
- 动态:同一个变量可以保存不同类型的数据
内存空间
js 内存模型包含:代码空间、栈空间、堆空间
- 原始类型是直接保存在栈空间中的,会随着当前栈元素的弹出而销毁
- 引用类型的值是存在堆空间的
栈空间都不会设置太大,主要用来存放一些原始类型的小数据
堆空间很大,能存放很多大的数据
在谈闭包
代码同上一模块的闭包
- 当JavaScript引擎执行到foo函数时,首先会编译,并创建一个空执行上下文。
- 在编译过程中,遇到内部函数setName,JavaScript引擎还要对内部函数做一次快速的词法扫描,发现该内部函数引用了foo函数中的myName变量,由于是内部函数引用了外部函数的变量,所以JavaScript引擎判断这是一个闭包,于是在堆空间创建换一个“closure(foo)”的对象(这是一个内部对象,JavaScript是无法访问的),用来保存myName变量。
- 接着继续扫描到getName方法时,发现该函数内部还引用变量test1,于是JavaScript引擎又将test1添加到“closure(foo)”对象中。这时候堆中的“closure(foo)”对象中就包含了myName和test1两个变量了。
- 由于test2并没有被内部函数引用,所以test2依然保存在调用栈中。
垃圾回收机制
调用栈的数据回收
通过向下移动ESP来销毁该函数保存在栈中的执行上下文
堆的回收
将会用到垃圾回收器
代际假说
- 大部分对象在内存中存在的时间很短,
- 不死的对象会活得更久
新生代垃圾回收器(副垃圾回收器)
Scavenge算法:新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域
- 对象区域快被写满时,就需要执行一次垃圾清理
- 对对象区域中的垃圾做标记
- 标记完成之后,就进入垃圾清理阶段,把这些存活的对象复制到空闲区域,并排列起来(内存整理)
- 完成复制后,对象区域与空闲区域进行角色翻转
为了执行效率,一般新生区的空间会被设置得比较小。
因为新生代空间小,容易存满,所以有 对象晋升策略
经过两次垃圾回收依然还存活的对象,会被移动到老生区中。
老生区(主垃圾回收器)
主垃圾回收器是采用标记-清除(Mark-Sweep)的算法进行垃圾回收
- 标记阶段从一组根元素开始,递归遍历,能达到的标记为活动对象,达不到的标记为垃圾数据
- 将标记为垃圾数据的清除
- 由于清除导致内存不连续,所以标记-整理(Mark-Compact)
全停顿
当对大量数据进行回收时,会造成整个主线程阻塞,所以增量标记(Incremental Marking)算法
编译器和解释器
编译型语言:c/c++ 、go
解释型语言:python、js
V8 是如何执行一段 js 代码的
V8在执行过程中既有解释器Ignition,又有编译器TurboFan
步骤
词法语法分析,生成抽象语法树(AST)和执行上下文
- 分词(tokenize),即词法分析
- 解析(parse),即语法分析
生成字节码
字节码就是介于AST和机器码之间的一种代码。
占用空间远远小于机器码
执行代码
即时编译(JIT)