V8 並沒有採用某種單一的技術,而是混合編譯執行和解釋執行這兩種手段,我們把這種混合使用編譯器和解釋器的技術稱爲 JIT(Just In Time)技術。
這是一種權衡策略,因爲這兩種方法都各自有各自的優缺點,解釋執行的啓動速度快,但是執行時的速度慢,而編譯執行的啓動速度慢,但是執行時的速度快。你可以參考下面完整的 V8 執行 JavaScript 的流程圖:
初始化基礎環境
- JavaScript 全局執行上下文就包含了執行過程中的全局信息,比如一些內置函數,全局變量等信息;
- 全局作用域包含了一些全局變量,在執行過程中的數據都需要存放在內存中;
- 而 V8 是採用了經典的堆和棧的內存管理模式,所以 V8 還需要初始化內存中的堆和棧結構;
- 另外,想要我們的 V8 系統活起來,還需要初始化消息循環系統,消息循環系統包含了消息驅動器和消息隊列,它如同 V8 的心臟,不斷接受消息並決策如何處理消息。
解析源碼生成 AST(抽象語法樹)
d8 --print-ast test.js
[generating bytecode for function: ]
--- AST ---
FUNC at 0
. KIND 0
. LITERAL ID 0
. SUSPEND COUNT 0
. NAME ""
. INFERRED NAME ""
. DECLS
. . VARIABLE (0x7fd47200fc90) (mode = VAR, assigned = true) "test"
. BLOCK NOCOMPLETIONS at -1
. . EXPRESSION STATEMENT at 11
. . . INIT at 11
. . . . VAR PROXY unallocated (0x7fd47200fc90) (mode = VAR, assigned = true) "test"
. . . . LITERAL "sullay"
解析源碼生成作用域
d8 --print-scopes test.js
Global scope:
global { // (0x7fe60004e248) (0, 19)
// will be compiled
// 1 stack slots
// temporary vars:
TEMPORARY .result; // (0x7fe60004e5a8) local[0]
// local vars:
VAR test; // (0x7fe60004e490)
}
依據AST和作用域生成字節碼
d8 --print-bytecode test.js
[generated bytecode for function: (0x16d008292e4d <SharedFunctionInfo>)]
Bytecode length: 18
Parameter count 1
Register count 3
Frame size 24
OSR nesting level: 0
Bytecode Age: 0
0x16d008292ed6 @ 0 : 13 00 LdaConstant [0]
0x16d008292ed8 @ 2 : c2 Star1
0x16d008292ed9 @ 3 : 19 fe f8 Mov <closure>, r2
0x16d008292edc @ 6 : 64 4f 01 f9 02 CallRuntime [DeclareGlobals], r1-r2
0x16d008292ee1 @ 11 : 13 01 LdaConstant [1]
0x16d008292ee3 @ 13 : 23 02 00 StaGlobal [2], [0]
0x16d008292ee6 @ 16 : 0e LdaUndefined
0x16d008292ee7 @ 17 : a8 Return
Constant pool (size = 3)
0x16d008292ea1: [FixedArray] in OldSpace
- map: 0x16d008002205 <Map>
- length: 3
0: 0x16d008292e95 <FixedArray[1]>
1: 0x16d008292e2d <String[6]: #sullay>
2: 0x16d00820c015 <String[4]: #test>
Handler Table (size = 0)
Source Position Table (size = 0)
優化熱點代碼爲二進制的機器代碼
生成字節碼之後,解釋器會解釋執行這段字節碼,如果重複執行了某段代碼,監控器就會將其標記爲熱點代碼,並提交給編譯器優化執行。
d8 --trace-opt test.js
反優化生成的二進制機器代碼
JavaScript 是一門動態語言,在運行過程中,某些被優化的結構可能會被 V8 動態修改了,這會導致之前被優化的代碼失效,如果某塊優化之後的代碼失效了,那麼編譯器需要執行反優化操作
d8 --trace-deopt test.js