Chrome V8 與 Node.js

從某種意義上來說,Node.js 並不是一個從零開始編寫的 JavaScript 運行時,它其實也是站在“巨人的肩膀”上進行了一系列的拼湊和封裝得到的結果。它的高效離不開一些很牛的第三方程序和類庫。比如本文我們介紹的Chrome V8。

Chrome V8 簡稱 V8,是由谷歌開源的一個高性能 JavaScript 引擎。該引擎採用 C++ 編寫,Google Chrome 瀏覽器用的就是這個引擎。V8 可以單獨運行,也可以嵌入 C++ 應用當中。

Chrome V8 與 Node.js

和其他的 JavaScript 引擎一樣,V8 會編譯、執行 JavaScript 代碼,並一樣會管理內存、垃圾回收等。

就是因為 V8 的高性能以及跨平臺等特性,所以它也是 Node.js 的 JavaScript 引擎。

高效

V8 開發小組由一群程序語言專家組成。其中核心工程師 Lars Bak 之前在 Sun 公司工作,專注於 Java 虛擬機加速技術的研究,產出了 HotSpot,除此之外,他還曾開發了 Strongtalk1。

所以,V8 的代碼裡面蘊含了從 HotSpot 和 Strongtalk 中汲取的精髓。

該研發小組從 2006 年開始研發 V8,原因是當年市面上的各種 JavaScript 引擎效率都比較低下。在 Lars Bak 等人的貢獻下,JavaScript 引擎添加了新的一員—— Chrome V8,並且效率非常高。

V8 的高效主要體現在以下 4 個特性上面。

(1) JIT 編譯

JIT 編譯,全稱 Just-In-Time 編譯,也就是即時編譯。它編譯出的結果直接是機器語言,而不是字節碼。這樣大大提高了 V8 在執行 JavaScript 時的效率。不過後來其他的幾家 JavaScript引擎也漸漸推出了對 JIT 的支持。

(2) 垃圾回收

這個特性在 Java 領域中使用得比較多。雖然其他語言或者其他的 JavaScript 引擎實現都有垃圾回收,但是 V8 的垃圾回收借鑑了 Java VM 的精確垃圾回收管理,而其他很多語言的垃圾回收用的是保守垃圾管理。

相較而言,V8 的這套垃圾回收機制的效率要遠遠高於其他一些垃圾回收機制實現——實際上代價就是這種機制的實現難度更大。

(3) 內聯緩存(Inline Cache)

V8 使用了內聯緩存的特性來提高屬性的訪問效率。如有一個訪問是 this. 蛋花湯,沒有內聯緩存的時候,每次要取蛋花湯的話都會對哈希表進行一次尋址,而加入了內聯緩存的特性之後,V8 能馬上知道這個屬性的一個偏移量,而不用再次計算尋址的偏移量了。

(4) 隱藏類

由於 JavaScript 是一門動態的編程語言,因此哪怕是在 ES6 及以上版本的規範中有了class 的一個定義,開發者也能非常方便地對一個對象添加或者移除一個屬性。

隱藏類就是對這樣一套對象體系中的一個黑科技的包裝——所有如屬性一樣的對象會被歸為同一個隱藏類。

下面舉個簡單的例子:

Chrome V8 與 Node.js

一開始根據 Pet 創建了 蛋花湯 這個對象。在最開始初始化的時候 V8 就會創建一個隱藏類(假設是 P0),這是一個空類,因為它還沒有任何的屬性;後來 this.type = type 執行了,隱藏類就有了 type 屬性,這個時候就又多了一個 P1 的隱藏類——P1 是基於 P0 創建的,並且多了 type 屬性;接著,name 被賦值上去,於是隱藏類又多了一個 P2。

然後在創建 南瓜餅 對象的時候,又走了上面的老路,只不過這次不是創建隱藏類 P0、P1和 P2 了,而是直接沿用它們。在初始化 南瓜餅 的時候,它依次會屬於上面創建的3 個隱藏類,直到最後它跟 蛋花湯 一樣都屬於 P2。

最後一行代碼在給 蛋花湯 賦值 age 的時候,又一個新的隱藏類 P3 會被創建。這個時候 蛋花湯 和 南瓜餅 分別屬於 P3 和 P2。這些描述分別如下圖。

Chrome V8 與 Node.js

最開始的蛋花湯和南瓜餅隱藏類歸屬

Chrome V8 與 Node.js

賦值 type 後的蛋花湯和南瓜餅隱藏類歸屬

Chrome V8 與 Node.js

賦值 name 後的蛋花湯和南瓜餅隱藏類歸屬

Chrome V8 與 Node.js

最終的蛋花湯和南瓜餅隱藏類歸屬

隱藏類和內聯緩存這兩把“匕首”聯合起來,是 V8 高效的一個非常重要的原因,因為同一個隱藏類的對象們能用同一套內聯緩存來尋址。

遵循 ECMAScript

在當前 V8 的項目主頁中,有一句話表明了它是遵循 ECMA-262 標準的:

“V8 implements ECMAScript as specified in ECMA-262.”

就目前來說 ECMA-262 標準(曾)發佈了 7 個大版本。

Chrome V8 與 Node.js

Chrome V8 與 Node.js

Generator 函數的愛稱,因其有一個顯著的標識——形如菊花的星號(*)而得名。

V8 在開發的過程中也一直追著 ECMAScript 發佈的腳步,如基本上完成了對 ES6 的支持,而且最新版也對 async/await 函數進行了支持。

也正是因為 V8 對 ECMAScript 標準緊追不捨,才有了 Node.js 能及時跟上 ECMAScript 最新語法的情況。如 Node.js 7.6 正式默認支持 async/await 功能就是沾了 V8 的光。

Node.js 與 Chrome V8

下面是 V8 與 Node.js 的部分版本對照表。

Chrome V8 與 Node.js

Chrome V8 與 Node.js

Node.js 一直緊跟 V8 的版本腳步在迭代。

Node.js 與 V8 實際上看起來更像是一對情侶,而不僅僅是 Node.js 一廂情願地使用 V8 作為自己的底層支持。

在 Chrome V8 的博客中曾經有一篇文章名為《V8 ❤ Node.js》。Node.js 在幾年發展中的流行度穩步增長,於是有了 V8 的“姑娘,你成功引起了我的注意”。現在 V8 也有一些工作是為 Node.js 而做的:

  • 在 Chrome 開發者工具中可以調試 Node.js;
  • 加速 ES6;
  • 針對 Node.js vm 模塊和 REPL 的一些修復;
  • Async / await。


分享到:


相關文章: