JavaScript、Node.js與V8的關係

對於瞭解Node的開發人員,咱們都知道Node是基於Chrome V8引擎開發的能使JavaScript在服務器端運行的運行時環境(runtime environment)。一方面,它提供了多種可調用的API,如讀寫文件、網絡請求、系統信息等。另外一方面,由於CPU執行的是機器碼,它還負責將JavaScript代碼解釋成機器指令序列執行,這部分工做是由V8引擎完成。node

Motivation

JavaScript 是一款擁有「自動垃圾回收」功能的編程語言。編程

市面上具備這樣功能的語言,通常都是擁有相對應的虛擬機的,像 JavaJVMC#CLRPHPZend數組

虛擬機通常實現了代碼解析,內存的管理、佈局、垃圾回收等功能。瀏覽器

不像C/C++這種沒有虛擬機的語言,它們須要手動管理內存。服務器

C/C++語言編譯後的文件,是能夠直接運行的。網絡

我認爲學習一門開發語言,除了知道一些語法上的使用,各類API的調用之外。學習相應的虛擬機也是頗有必要的。而 JavaScript 因爲其特殊的歷史緣由,並非只有 V8 一個引擎。可是目前 V8 它是業界最優秀的 JavaScript 引擎,也就成爲了一個學習樣本。併發

現在的 JavaScript 不只僅是用在瀏覽器端了,也由於 NodeJS 的關係得以在服務器端運行。和瀏覽器端不一樣的地方在於服務器端對資源的敏感性是很高的。當業務規模大了,併發量上來了,一些很細小的問題會放大。這時候一些小小的內存泄漏,都會釀造災難。編程語言

因此做爲一個 JavaScript 開發者,搞清楚從敲入 console.log('hello world') ,直到後面交由CPU執行的中間過程是很重要的。工具

這也對如何用 JavaScript 這門鬆散的語言編寫出高質量的代碼是具備指導做用的。佈局

想真正作到 JavaScript 全棧,路漫漫其修遠兮。

NodeJS 概述

根據百度百科解釋,Node.js是一套用來編寫高性能網絡服務器的JavaScript工具包。Node.js是一個能夠快速構建網絡服務及應用的平臺,該平臺的構建是基於Chrome's JavaScript runtime,也就是說,實際上它是對GoogleV8引擎(應用於Google Chrome瀏覽器)進行了封裝。V8引 擎執行Javascript的速度很是快,性能很是好。

NodeJS並非提供簡單的封裝,而後提供API調用,若是是這樣的話那麼它就不會有如今這麼火了。Node對一些特殊用例進行了優化,提供了替代的API,使得V8在非瀏覽器環境下運行得更好。例如,在服務器環境中,處理二進制數據一般是必不可少的,但Javascript對此支持不足,所以,V8.Node增長了Buffer類,方便而且高效地 處理二進制數據。所以,Node不只僅簡單的使用了V8,還對其進行了優化,使其在各環境下更加給力。

即時編譯JIT 概述

V8採用即時編譯技術(JIT),直接將JavaScript代碼編譯成本地平臺的機器碼。宏觀上看,其步驟爲JavaScript源碼—>抽象語法樹—>本地機器碼,而且後一個步驟只依賴前一個步驟。這與其餘解釋器不一樣,例如Java語言須要先將源碼編譯成字節碼,而後給JVM解釋執行,JVM根據優化策略,運行過程當中有選擇地將一部分字節碼編譯成本地機器碼。V8不生成中間代碼,一步到位,編譯成機器碼,CPU就開始執行了。比起生成中間碼解釋執行的方式,V8的策略省去了一個步驟,程序會更早地開始運行。而且執行編譯好的機器指令,也比解釋執行中間碼的速度更快。不足的是,缺乏字節碼這個中間表示,使得代碼優化變得更困難。

V8 概述

V8 做爲一個 JavaScript 引擎,最初是服役於 Google Chrome 瀏覽器的。它隨着 Chrome 的初版發佈而發佈以及開源。如今它除了 Chrome 瀏覽器,已經有不少其餘的使用者了。諸如 NodeJSMongoDBCouchDB 等。

JavaScript 做爲 Prototype-Based Language , 基於它使用 Prototype 繼承的特徵,V8 使用了直譯的方式,即把 JavaScript 代碼直接編譯成機器碼( Machine Code, 有些地方也叫 Native Code ),而後直接交由硬件執行。

與傳統的「編譯-解析-執行」的流程不一樣,V8 處理 JavaScript,並無二進制碼或其餘的中間碼。

簡單來講,V8主要工做就是:「把 JavaScript 直譯成機器碼,而後運行」

但這中間,每每是一個複雜的過程,它須要處理不少的難題,諸如:

編譯優化

內存管理

垃圾回收

V8 In NodeJS/NodeJS源碼小覽

NodeJS,是怎麼引入V8的?

咱們關注 Node的源碼 目錄:

.
├── ...
├── deps
│   ├── ...
│   ├── v8
│   ├── ...
├── ...
├── lib
│   ├── ...
│   ├── buffer.js
│   ├── child_process.js
│   ├── console.js
│   ├── ...
├── node -> out/Release/node
├── ...
├── out
│   ├── ...
│   ├── Release
|         ├── node
|         ├── node.d
|         ├── obj
|             └── gen
|                 ├── ...
|                 ├── node_natives.h
|                 ├── ...
│   ├── ...
├── src
│   ├── ...
│   ├── debug-agent.cc
│   ├── debug-agent.h
│   ├── env-inl.h
│   ├── env.cc
│   ├── ...
├── 
...

須要關注的幾個目錄和文件:

/deps/v8 :這裏是V8源碼所在文件夾,你會發現裏面的目錄結構跟 V8源碼 十分類似。NodeJS除了移植V8源碼,還在增添了一些內容。

/src :由C/C++編寫的核心模塊所在文件夾,由C/C++編寫的這部分模塊被稱爲「Builtin Module」

/lib :由JavaScript編寫的核心模塊所在文件夾,這部分被稱爲「Native Code」,在編譯Node源碼的時候,會採用V8附帶的 js2c.py 工具,把全部內置的JavaScript代碼轉換成C++裏面的數組,生成 out/Release/obj/gen/node_natives.h 文件。有些 Native Module 須要藉助於 Builtin Module 實現背後的功能。

/out :該目錄是Node源碼編譯(命令行運行 make )後生成的目錄,裏面包含了Node的可執行文件。當在命令行中鍵入 node xxx.js ,實際就是運行了 out/Release/node 文件。

來張圖說明一下V8在Node運行時的總體過程。

clipboard.png

Node在啓動的時候,就已經把 Native Module,Builtin Module 加載到內存裏面了。後來的 JavaScript 代碼,就須要經過 V8 進行動態編譯解析運行。

相關文章
相關標籤/搜索