对于了解Node
的开发人员,咱们都知道Node
是基于Chrome V8
引擎开发的能使JavaScript
在服务器端运行的运行时环境(runtime
environment
)。一方面,它提供了多种可调用的API
,如读写文件、网络请求、系统信息等。另外一方面,由于CPU
执行的是机器码,它还负责将JavaScript
代码解释成机器指令序列执行,这部分工做是由V8引擎
完成。node
JavaScript
是一款拥有「自动垃圾回收」功能的编程语言。编程
市面上具备这样功能的语言,通常都是拥有相对应的虚拟机的,像 Java
的JVM
,C#
的CLR
,PHP
的Zend
。数组
虚拟机通常实现了代码解析,内存的管理、布局、垃圾回收等功能。浏览器
不像C/C++
这种没有虚拟机的语言,它们须要手动管理内存。服务器
C/C++
语言编译后的文件,是能够直接运行的。网络
我认为学习一门开发语言,除了知道一些语法上的使用,各类API
的调用之外。学习相应的虚拟机也是颇有必要的。而 JavaScript
因为其特殊的历史缘由,并非只有 V8
一个引擎。可是目前 V8
它是业界最优秀的 JavaScript
引擎,也就成为了一个学习样本。并发
现在的 JavaScript
不只仅是用在浏览器端了,也由于 NodeJS
的关系得以在服务器端运行。和浏览器端不一样的地方在于服务器端对资源的敏感性是很高的。当业务规模大了,并发量上来了,一些很细小的问题会放大。这时候一些小小的内存泄漏,都会酿造灾难。编程语言
因此做为一个 JavaScript
开发者,搞清楚从敲入 console.log('hello world')
,直到后面交由CPU
执行的中间过程是很重要的。工具
这也对如何用 JavaScript
这门松散的语言编写出高质量的代码是具备指导做用的。布局
想真正作到 JavaScript
全栈,路漫漫其修远兮。
根据百度百科解释,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
,还对其进行了优化,使其在各环境下更加给力。
V8
采用即时编译技术(JIT
),直接将JavaScript
代码编译成本地平台的机器码。宏观上看,其步骤为JavaScript
源码—>抽象语法树—>本地机器码,而且后一个步骤只依赖前一个步骤。这与其余解释器不一样,例如Java
语言须要先将源码编译成字节码,而后给JVM
解释执行,JVM
根据优化策略,运行过程当中有选择地将一部分字节码编译成本地机器码。V8
不生成中间代码,一步到位,编译成机器码,CPU
就开始执行了。比起生成中间码解释执行的方式,V8
的策略省去了一个步骤,程序会更早地开始运行。而且执行编译好的机器指令,也比解释执行中间码的速度更快。不足的是,缺乏字节码这个中间表示,使得代码优化变得更困难。
V8
做为一个 JavaScript
引擎,最初是服役于 Google Chrome
浏览器的。它随着 Chrome
的初版发布而发布以及开源。如今它除了 Chrome
浏览器,已经有不少其余的使用者了。诸如 NodeJS
、MongoDB
、CouchDB
等。
JavaScript
做为 Prototype-Based Language
, 基于它使用 Prototype
继承的特征,V8
使用了直译的方式,即把 JavaScript
代码直接编译成机器码( Machine Code
, 有些地方也叫 Native Code
),而后直接交由硬件执行。
与传统的「编译-解析-执行」的流程不一样,V8
处理 JavaScript
,并无二进制码或其余的中间码。
简单来讲,V8
主要工做就是:「把 JavaScript
直译成机器码,而后运行」
但这中间,每每是一个复杂的过程,它须要处理不少的难题,诸如:
编译优化 内存管理 垃圾回收
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运行时的总体过程。
Node在启动的时候,就已经把 Native Module,Builtin Module 加载到内存里面了。后来的 JavaScript 代码,就须要经过 V8 进行动态编译解析运行。