作為JavaScript欄目開發(fā)人員,深入了解 JavaScript 引擎的工作原理有助于你了解自己代碼的性能特征。這篇文章對所有 JavaScript 引擎中常見的一些關(guān)鍵基礎(chǔ)知識(shí)進(jìn)行了介紹,不僅僅局限于 V8 引擎。
JavaScript 引擎的工作流程 (pipeline)
這一切都要從你寫的 JavaScript 代碼開始。JavaScript 引擎解析源代碼并將其轉(zhuǎn)換為抽象語法樹(AST)?;?AST,解釋器便可以開始工作并生成字節(jié)碼。就在此時(shí),引擎開始真正地運(yùn)行 JavaScript 代碼。為了讓它運(yùn)行得更快,字節(jié)碼能與分析數(shù)據(jù)一起發(fā)送到優(yōu)化編譯器。優(yōu)化編譯器基于現(xiàn)有的分析數(shù)據(jù)做出某些特定的假設(shè),然后生成高度優(yōu)化的機(jī)器碼。
如果某個(gè)時(shí)刻某一個(gè)假設(shè)被證明是不正確的,那么優(yōu)化編譯器將取消優(yōu)化并返回到解釋器階段。
JavaScript 引擎中的解釋器/編譯器工作流程
現(xiàn)在,讓我們來看實(shí)際執(zhí)行 JavaScript 代碼的這部分流程,即代碼被解釋和優(yōu)化的部分,并討論其在主要的 JavaScript 引擎之間存在的一些差異。
一般來說,JavaSciript 引擎都有一個(gè)包含解釋器和優(yōu)化編譯器的處理流程。其中,解釋器可以快速生成未優(yōu)化的字節(jié)碼,而優(yōu)化編譯器會(huì)耗費(fèi)更長的時(shí)間,但最終可生成高度優(yōu)化的機(jī)器碼。這個(gè)通用流程和 Chrome 和 Node.js 中使用的 Javascript 引擎, V8 的工作流程幾乎一致:
V8 中的解釋器稱為 Ignition,負(fù)責(zé)生成和執(zhí)行字節(jié)碼。當(dāng)它運(yùn)行字節(jié)碼時(shí),它收集分析數(shù)據(jù),這些數(shù)據(jù)可用于后面加快代碼的執(zhí)行速度。當(dāng)一個(gè)函數(shù)變?yōu)?hot 時(shí),例如當(dāng)它經(jīng)常運(yùn)行時(shí),生成的字節(jié)碼和分析數(shù)據(jù)將傳遞給我們的優(yōu)化編譯器 Turbofan,以根據(jù)分析數(shù)據(jù)生成高度優(yōu)化的機(jī)器代碼。
Mozilla 在 Firefox 和 Spidernode 中使用的 JavaScript 引擎 SpiderMonkey ,則不太一樣。它們有兩個(gè)優(yōu)化編譯器,而不是一個(gè)。解釋器先通過 Baseline 編譯器,生成一些優(yōu)化的代碼。然后,結(jié)合運(yùn)行代碼時(shí)收集的分析數(shù)據(jù),IonMonkey 編譯器可以生成更高程度優(yōu)化的代碼。如果嘗試優(yōu)化失敗,IonMonkey 將返回到 Baseline 階段的代碼。
Chakra,在 Edge 中使用的 Microsoft 的 JavaScript 引擎,非常相似的,也有2個(gè)優(yōu)化編譯器。解釋器優(yōu)化代碼到 SimpleJIT(JIT 代表 Just-In-Time 編譯器,即時(shí)編譯器),SimpleJIT 會(huì)生成稍微優(yōu)化的代碼。而 FullJIT 結(jié)合分析數(shù)據(jù),可以生成更為優(yōu)化的代碼。JavaScriptCore(縮寫為 JSC),在 Safari 和 React Native 中使用的 Apple 的 JavaScript 引擎,它通過三種不同的優(yōu)化編譯器將其發(fā)揮到極致。低層解釋器 LLInt 優(yōu)化代碼到 Baseline 編譯器中,然后優(yōu)化代碼到 DFG(Data Flow Graph)編譯器中,DFG(Data Flow Graph)編譯器又可以將優(yōu)化后的代碼傳到 FTL(Faster Than Light)編譯器中。
為什么有些引擎有