走進chrome心裏,瞭解V8引擎是如何工做的

做爲一個前端程序員,天天上班的第一件事就是打開電腦,不禁自主的點開chrome瀏覽器,或是摸會兒魚或是立馬進入工做狀態。接下來瀏覽器窗口就會陪伴着你度過一天的時光,正常到七八點鐘,晚點就九十點鐘,再晚點就陪你跨過一天,時刻關注着你的工做。做爲一個忠誠陪伴你的夥伴,你捫心自問,你有認真的瞭解過它是如何工做的嗎?你有走進過它的心裏世界嗎?前端

若是你也好奇過,那麼請收看這期的《走進chrome心裏,瞭解V8引擎是如何工做的》。程序員

V8是什麼

在深刻了解一件事物以前,首先要知道它是什麼。chrome

V8是一個由Google開源的採用C++編寫的高性能JavaScriptWebAssembly引擎,應用在 ChromeNode.js等中。它實現了ECMAScriptWebAssembly,運行在Windows 7及以上、macOS 10.12+以及使用x6四、IA-3二、ARMMIPS處理器的Linux系統上。 V8能夠獨立運行,也能夠嵌入到任何C++應用程序中。segmentfault

V8由來

接下來咱們來關心關心它如何誕生的,以及爲何叫這個名字。瀏覽器

V8最初是由Lars Bak團隊開發的,以汽車的V8發動機(有八個氣缸的V型發動機)進行命名,預示着這將是一款性能極高的JavaScript引擎,在2008年9月2號chrome一同開源發佈。架構

爲何須要V8

咱們寫的JavaScript代碼最終是要在機器中被執行的,但機器沒法直接識別這些高級語言。須要通過一系列的處理,將高級語言轉換成機器能夠識別的的指令,也就是二進制碼,交給機器執行。這中間的轉換過程就是V8的具體工做。ide

接下來咱們就來詳細的瞭解一下。函數

V8組成

首先來看一下V8的內部組成。V8的內部有不少模塊,其中最重要的4個以下:性能

  • Parser: 解析器,負責將源代碼解析成AST
  • Ignition: 解釋器,負責將AST轉換成字節碼並執行,同時會標記熱點代碼
  • TurboFan: 編譯器,負責將熱點代碼編譯成機器碼並執行
  • Orinoco: 垃圾回收器,負責進行內存空間回收

V8工做流程

如下是V8中幾個重要模塊的具體工做流程圖。咱們逐個分析。優化

V8工做流程.png

Parser解析器

Parser解析器負責將源代碼轉換成抽象語法樹AST。在轉換過程當中有兩個重要的階段:詞法分析(Lexical Analysis)語法分析(Syntax Analysis)

詞法分析

也稱爲分詞,是將字符串形式的代碼轉換爲標記(token)序列的過程。這裏的token是一個字符串,是構成源代碼的最小單位,相似於英語中單詞。詞法分析也能夠理解成將英文字母組合成單詞的過程。詞法分析過程當中不會關心單詞之間的關係。好比:詞法分析過程當中可以將括號標記成token,但並不會校驗括號是否匹配。

JavaScript中的token主要包含如下幾種:

關鍵字:var、let、const等

標識符:沒有被引號括起來的連續字符,多是一個變量,也多是 if、else 這些關鍵字,又或者是 true、false 這些內置常量

運算符: +、-、 *、/ 等

數字:像十六進制,十進制,八進制以及科學表達式等

字符串:變量的值等

空格:連續的空格,換行,縮進等

註釋:行註釋或塊註釋都是一個不可拆分的最小語法單元

標點:大括號、小括號、分號、冒號等

如下是const a = 'hello world'通過esprima詞法分析後生成的tokens

[
    {
        "type": "Keyword",
        "value": "const"
    },
    {
        "type": "Identifier",
        "value": "a"
    },
    {
        "type": "Punctuator",
        "value": "="
    },
    {
        "type": "String",
        "value": "'hello world'"
    }
]
語法分析

語法分心是將詞法分析產生的token按照某種給定的形式文法轉換成AST的過程。也就是把單詞組合成句子的過程。在轉換過程當中會驗證語法,語法若是有錯的話,會拋出語法錯誤。

上述const a = 'hello world'通過語法分析後生成的AST以下:

{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": {
            "type": "Identifier",
            "name": "a"
          },
          "init": {
            "type": "Literal",
            "value": "hello world",
            "raw": "'hello world'"
          }
        }
      ],
      "kind": "const"
    }
  ],
  "sourceType": "script"
}

通過Parser解析器生成的AST將交由Ignition解釋器進行處理。

Ignition解釋器

Ignition解釋器負責將AST轉換成字節碼(Bytecode)並執行。字節碼是介於AST和機器碼之間的一種代碼,與特定類型的機器代碼無關,須要經過解釋器轉換成機器碼才能夠執行。

看到這裏想必你們都有疑惑,既然字節碼也須要轉換成機器碼才能運行,那一開始爲何不直接將AST轉換成機器碼直接運行呢?轉換成機器碼直接運行速度確定更快,那爲何還要加一箇中間過程呢?

其實在V85.9版本以前是沒有字節碼的,而是直接將JS代碼編譯成機器碼並將機器碼存儲到內存中,這樣就佔用了大量的內存,而早期的手機內存都不高,過分的佔用會致使手機性能大大的降低;並且直接編譯成機器碼致使編譯時間長,啓動速度慢;再者直接將JS代碼轉換成機器碼須要針對不一樣的CPU架構編寫不一樣的指令集,複雜度很高。

5.9版本之後引入了字節碼,能夠解決上述內存佔用大、啓動時間長、代碼複雜度高這幾個問題。

接下來咱們來看看Ignition是如何將AST轉換成字節碼的。

下圖是Ignition解釋器的工做流程圖。AST須要先經過字節碼生成器,再通過一系列的優化以後才能生成字節碼。

ignation.png

其中的優化包括:

  • Register Optimizer:主要是避免寄存器沒必要要的加載和存儲
  • Peephole Optimizer:尋找字節碼中能夠複用的部分,並進行合併
  • Dead-code Elimination: 刪除無用的代碼,減小字節碼的大小

將代碼轉換成字節碼後就能夠經過解釋器執行了。Ignition在執行的過程當中,會監視代碼的執行狀況並記錄執行信息,如函數的執行次數、每次執行函數時所傳的參數等。

當同一段代碼被執行屢次,就會被標記成熱點代碼。熱點代碼會交給TurboFan編譯器進行處理。

TurboFan編譯器

TurboFan拿到Ignition標記的熱點代碼後,會先進行優化處理,而後將優化後字節碼編譯成更高效的機器碼存儲起來。下次再次執行相同代碼時,會直接執行相應的機器碼,這樣就在很大程度上提高了代碼的執行效率。

當一段代碼再也不是熱點代碼後,TurboFan會進行去優化的過程,將優化編譯後的機器碼還原成字節碼,將代碼的執行權利交還給Ignition

如今咱們來看一看具體的執行過程。

sum += arr[i]爲例,因爲JS是動態類型的語言,每次的sumarr[i]都有多是不一樣的類型,在執行這段代碼時,Ignition每次都會檢查sumarr[i]的數據類型。當發現一樣的代碼被執行了屢次時,就將其標記爲熱點代碼,交給TurboFan

TurboFan在執行時,若是每次都判斷sumarr[i]的數據類型是很浪費時間的。所以在優化時,會根據以前的幾回執行肯定sumarr[i]的數據類型,將其編譯成機器碼。下次再執行時,省去了判斷數據類型的過程。

但若是在後續的執行過程當中,arr[i]的數據類型發生了改變,以前生成的機器碼就不知足要求了,TurboFan會把以前生成的機器碼丟棄,將執行權利再交給Ignition,完成去優化的過程。

熱點代碼:
image.png

優化前:
image.png

優化後:
image.png

總結

如今咱們來總結一下V8的執行過程:

  1. 源代碼通過Parser解析器,通過詞法分析和語法分析生成AST
  2. AST通過Ignition解釋器生成字節碼並執行
  3. 在執行過程當中,若是發現熱點代碼,將熱點代碼交給TurboFan編譯器生成機器碼並執行
  4. 若是熱點代碼再也不知足要求,進行去優化處理

這種字節碼與解釋器和編譯器結合的技術,就是咱們一般所說的即時編譯(JIT)。

本文並無介紹垃圾回收器OrinocoV8的垃圾回收機制能夠單獨用一篇文章來詳細介紹,咱們下期再見。

參考文章

  1. V8官方文檔
  2. Celebrating 10 years of V8
  3. V8是如何執行JavaScript代碼的?
  4. Ignition: An Interpreter for V8
  5. 即時編譯
相關文章
相關標籤/搜索