快速了解V8引擎如何启动

36 阅读3分钟

怎么启动的?

肯定有初始化环境,肯定要经历编译那些事儿生成字节码,然后交给解释器就完事啦,当然还要有热点代码的优化...

好像没什么问题,但未免也太笼统简单了T.T

结合笔者最近学习内容,想要填充V8启动步骤和分享出来,如有不足,欢迎评论区留言指出

初始化了啥?

首先要明白,V8引擎需要宿主环境,就像PPT运行在操作系统上一样,操作系统不能写PPT内容,但是操作系统为PPT提供环境。V8引擎不是直接扔进去JS代码,就能自己凭空产生空间和靠自己超能力感知外部变化的。

所以宿主环境,如诸多浏览器引擎、诸多非浏览器运行时、诸多编程语言(笔者发现很多语言都能嵌入V8,可以看看自己中意的语言能不能嵌入V8)等等,只要用V8引擎的,都需要提供宿主环境。 那需要哪些?需要调用栈,堆,回调队列,事件循环,其它基础功能部件..

我们用浏览器举例,浏览器的某个渲染进程实例化了V8实例,然后开了片连续地址的内存空间--栈和零零散散的内存大空间--堆,以及构造事件循环系统。

怎么到字节码的?

我们知道它们之间有一段不解之缘,但在这里光说怎么到达字节码。 我们需要先编译来获取AST,同时生成作用域。 获取AST这步常见,先语法分析,然后词法分析,然后语义分析生成抽象语法树。

生成作用域呢? 首先了解动态作用域和静态作用域,

let name = "111"
function a() {console.log(name)};
function b() {let name = "222"; a()};
b()

简单来说,如果输出111,则为静态作用域,a函数根据声明位置,来判断它的上下文。 如果输出222,则为动态作用域,a函数根据被调用位置,来判断它的上下文。

在js中,明显是前者--静态作用域。所以生成作用域,就是根据声明位置来判断上下文,为后续处理提供一个导航指引工具。

关于静态作用域关系怎么存?

我们知道调用栈中的执行上下文,有四个结构:变量环境、词法环境、outer、this。当在咱们现在所说的编译步骤中,就是把a的上下文的outer指向最外面上下文,同样b的上下文outer也指向最外面上下文

再说说别的结构:this就是这个函数被调用的对象,变量环境是存入var声明和function声明的变量,词法环境是以栈的方式,以块作用域为分组来存入变量的。

在拥有AST和作用域两大法宝后,生成字节码。 题外话,中间码这一步可以决定是不是解释型或编译型,基本到AST这步,就能让解释器明白指令意思啦

解释字节码和优化编译

通常执行字节码有两种方式,一个是基于栈(JVM那种),一个是基于寄存器。V8引擎选择寄存器方式,具体可看这里

相信优化编译已经被讲无数次,就是检测到热点字节码,然后被转化成高效机器码...

结尾

嘿嘿,大致启动就是这样啦,主要是在初始化和作用域,后续有机会,分享更多V8的知识~