浏览器工作原理和V8引擎

236 阅读4分钟

一、简单介绍浏览器内核和js引擎

浏览器内核:浏览器内核解析文件,负责HTML解析、布局、渲染等等相关的工作 javascript引擎:用来将Jvascript代码翻译成CPU指令来执行

浏览器内核和JS引擎的关系: 以WebKit为例,WebKit(是一种浏览器内核)事实上由两部分组成的 WebCore:负责HTML解析、布局、渲染等等相关的工作 JavaScriptCore:解析、执行JavaScript代码

V8引擎(node,google中用的都是这个JS引擎)

  • V8引擎是用C++编写的开源高性能Javascript和WebAssembly引擎。
  • 它实现ECMAScript和WebAssembly,并在Windows7或更高版本,macOS10.12+和x64,IA-32ARM或MIPS处理器的Linux系统上运行(可以在多个平台使用)
  • 可以独立运行,也可以嵌入到任何C++应用程序中

二、V8引擎原理

前期介绍

image

  1. 把Javascript语言进行parse,然后得到抽象语法树(包括词法分析、语法分析)AST
  2. 通过Ignition转成字节码。(不确定跑在那种平台上,那能执行的机器指令是不一样的,因此需要先转成字节码)
  3. 然后还需要将字节码转成机器代码执行。

parse模块会将JavaScript代码转换成AST(抽象语法树)

  • 如果函数没有调用,那么是不会被转成AST,进行一个预解析就好了

Ignition是一个解释器,会将AST转换成ByteCode(字节码)

  • 同时会收集TurboFan优化所需要的信息(比如函数参数的类型信息,有了类型才能进行真实的运算)
  • 如果函数只调用一次,Ignition会执行解释执行ByteCode

TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码。

  • 如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过TurboFan转换优化的机器码,提高代码的执行性能。
  • 但是,机器码实际上也会被还原为ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(比如sum函数原来执行的是number类型,后来执行变成了string类型),之前优化的机器码并不能正确处理运算,就会逆向的转换成字节码。

官网流程:

image Blink内核解析html可能会遇到js,然后就下载js文件。Blink把js代码传给V8引擎。 V8先转换编码,然后通过Scanner转换成tokens,然后给Parser转换成AST,再把AST给Ignation转换成Bytecode。详细过程如下:

  • Blink将源码交给V8引擎,Stream获取到源码并且进行编码转换;
  • Scanner会进行词法分析(lexical analysis),词法分析会将代码转换成tokens;
  • 接下来tokens会被转换成AST树,经过Parser和PreParser:
    • Parser就是直接将tokens转成AST树架构;
    • PreParser称之为预解析,为什么需要预解析呢?
      • 这是因为并不是所有的JavaScript代码,在一开始时就会被执行。那么对所有的JavaScript代码进行解析,必然会影响网页的运行效率;
      • 所以V8引擎就实现了Lazy Parsing(延迟解析)的方案,它的用是将不必要的函数进行预解析,也就是只解析暂时需要的内容,而对函数的全量解析是在函数被调用时才会进行;
      • 比如我们在一个函数outer内部定义了另外一个函数inner,那么inner函数就会进行预解析
  • 生成AST树后,会被Ignition转成字节码( bytecode ),之后的过程就是代码的执行过程(后续会详细分析)。

执行代码

  1. 代码被解析,并且构建一个全局对象GlobalObject(GO),(包含String,setTimeout,Date等,window指向当前这个对象),解析js代码把全局变量、函数等放到全局对象中(变量提升)
  2. 运行代码
    1. v8为了执行代码,v8引擎内部会有一个执行上下文栈(Execution Context Stack,ECStack)(函数调用栈)

    2. 因为我们执行的是全局代码,为了全局代码能够正常执行,需要创建全局执行上下文(Global Execution Context,GEC)(全局代码需要被执行时才会创建) 内部有一个东西叫VO(variable Object):GO,它指向GO

    3. 将全局执行上下文放入到执行上下文栈开始一步步执行。