开篇词 | 如何学习谷歌高性能 JavaScript 引擎V8?

你好,我是李兵,《浏览器工作原理与实践》专栏的作者。在浏览器专栏中,我们对浏览器的工作原理进行了详细的介绍,其中也提到了V8是如何执行JavaScript代码的。很多朋友对这部分的学习意犹未尽,因此我又回到了极客时间,与你深入聊聊V8。

什么是V8?

V8是JavaScript虚拟机的一种。我们可以简单地把JavaScript虚拟机理解成是一个翻译程序,将人类能够理解的编程语言JavaScript,翻译成机器能够理解的机器语言。如下图所示:

上图中,中间的“黑盒”就是JavaScript引擎V8。目前市面上有很多种JavaScript引擎,诸如SpiderMonkey、V8、JavaScriptCore等。而由谷歌开发的开源项目V8是当下使用最广泛的JavaScript虚拟机,全球有超过25亿台安卓设备,而这些设备中都使用了Chrome浏览器,所以我们写的JavaScript应用,大都跑在V8上。

V8之所以拥有如此庞大的生态圈,也和它许多革命性的设计是分不开的。

在V8出现之前,所有的JavaScript虚拟机所采用的都是解释执行的方式,这是JavaScript执行速度过慢的一个主要原因。而V8率先引入了即时编译(JIT)的双轮驱动的设计,这是一种权衡策略,混合编译执行和解释执行这两种手段,给JavaScript的执行速度带来了极大的提升。

V8出现之后,各大厂商也都在自己的JavaScript虚拟机中引入了JIT机制,所以你会看到目前市面上JavaScript虚拟机都有着类似的架构。另外,V8也是早于其他虚拟机引入了惰性编译、内联缓存、隐藏类等机制,进一步优化了JavaScript代码的编译执行效率。

可以说,V8的出现,将JavaScript虚拟机技术推向了一个全新的高度。

即便V8具有诸多优点,但我相信对于大部分同学来说,V8虚拟机还只是一个黑盒,我们将一段代码丢给这个黑盒,它便会返回结果,并没有深入了解过它的工作原理。

如果只是单纯使用JavaScript和调用 Web API,并不了解虚拟机内部是怎样工作的,在项目中遇到的很多问题很可能找不到解决的途径。比如,有时项目的占用内存过高,或者页面响应速度过慢,又或者使用Node.js的时候导致任务被阻塞等问题,都与V8的基本运行机制有关。如果你熟悉V8的工作机制,就会有系统性的思路来解决这些问题。

另外,V8的主要功能,就是结合JavaScript语言的特性和本质来编译执行它。通过深入地学习V8,你对JavaScript语言本质和设计思想会有很直观的感受。这些设计思想像是更加高级的工具,你掌握了它,就可以提升你的语言使用和架构设计水平。

如何学习V8?

那么,我们应该如何来学习V8呢?

刚刚我们也说过,V8的主要职责是用来执行JavaScript代码的,那我们首先需要先了解JavaScript这门语言的基本特性和设计思想。

JavaScript借鉴了很多语言的特性,比如C语言的基本语法、Java的类型系统和内存管理、Scheme的函数作为一等公民,还有Self基于原型(prototype)的继承机制。毫无疑问,JavaScript是一门非常优秀的语言,特别是“原型继承机制”和“函数是一等公民”这两个设计。

不过JavaScript也是一门处处是坑的语言,由于历史原因,很多错误的或者不合理的设计都被延续至今,比如使用new加构造函数来创建对象,这种方式的背后隐藏了太多的细节,非常容易增加代码出错概率,而且也大大增加了新手的学习成本;再比如初期的JavaScript没有块级作用域机制,使得JavaScript需要采取变量提升的策略,而变量提升又是非常反人性的设计。

V8是JavaScript的实现,在学习V8工作原理时,我们就要格外关注JavaScript这些独特的设计思想和特性背后的实现。比如,为了实现函数是一等公民的特性,JavaScript采取了基于对象的策略;再比如为了实现原型继承,V8为每个对象引入了__proto__属性。

深入分析过JavaScript语言之后,我们就可以学习V8执行JavaScript代码的完整流程了。我们把这套流程称之为V8的编译流水线,其完整流程如下图所示:

编译流水线本身并不复杂,但是其中涉及到了很多技术,诸如JIT、延迟解析、隐藏类、内联缓存等等。这些技术决定着一段JavaScript代码能否正常执行,以及代码的执行效率。

比如V8中使用的隐藏类(Hide Class),这是将JavaScript中动态类型转换为静态类型的一种技术,可以消除动态类型的语言执行速度过慢的问题,如果你熟悉V8的工作机制,在你编写JavaScript时,就能充分利用好隐藏类这种强大的优化特性,写出更加高效的代码。

再比如,V8实现了JavaScript代码的惰性解析,目的是为了加速代码的启动速度,通过对惰性解析机制的学习,你可以优化你的代码更加适应这个机制,从而提高程序性能。

要想充分了解V8是怎么工作的,除了要分析编译流水线,我们还需要了解另外两个非常重要的特性,那就是事件循环系统垃圾回收机制。

事件循环系统和JavaScript中的难点——异步编程特性紧密相关。我们知道,JavaScript是单线程的,JavaScript代码都是在一个线程上执行,如果同一时间发送了多个JavaScript执行的请求,就需要排队,也就是进行异步编程。

V8的事件循环系统会调度这些排队任务,保证JavaScript代码被V8有序地执行。因此也可以说,事件循环系统就是V8的心脏,它驱动了V8的持续工作。

另外,JavaScript是一种自动垃圾回收的语言,V8在执行垃圾回收时,会占用主线程的资源,如果我们编写的程序频繁触发垃圾回收,那么无疑会阻塞主线程,这也是我们经常会遇到的一个问题。你需要知道V8是如何分配内存数据的,以及这些数据是如何被回收的,打通整个链路,建立完整的系统,当下次遇到内存问题时,就知道如何去排查了。

以上,就是系统学习V8的路径。在我们这一季的课程中,也会按照这样的思路来设计课程,来帮助你学习到V8的完整的知识体系。

  • 首先,我们会从JavaScript的设计思想讲起,讨论它背后的一些主要特性,以及V8是怎么实现这些特性的。
  • 然后,我们再来分析V8的编译流水线,在课程中间我们还会穿插介绍一些内存分配相关的内容,因为函数调用、变量声明、参数传递或者函数返回数值都涉及到了内存分配。
  • 最后,我们会介绍事件循环系统和垃圾回收系统的工作机制。

虽然本课程的篇幅不多,但是也具有一定的深度和广度。不过你并不需要担心内容太难,我会尽量将每节内容做到深入浅出,有什么问题你可以在留言区提问,我看到后都会第一时间来解答。

另外,在每个模块结束后,我会做一次热点问题的答疑,尽量帮你扫清学习V8的障碍。你需要做的是持之以恒地学习、反思与实践。

加油,从今天起,就让我们一起开始V8的学习旅程吧!

精选留言

  • Lorin

    2020-03-16 17:21:35

    老师,我大学是自学前端的,毕业后已经从事前端工作好几年了,但是还是感觉自己技术很水,这种怎么办?
    作者回复

    一步一步来:

    首先整体知识架构的学习不能断,慢慢持续积累,比如数据结构、操作系统、计算机网络、编译原理等。

    另外、公司的业务要积极完成,可以结合业务深入了解下背后的架构设计。

    最后、思维方式也特别重要、因为项目有多轻重缓急的任务、写代码的时候也会进程卡住一个地方,这时候就需要有个全局视角,哪些是重要的,哪些可以后期迭代。能能做权衡利弊、权衡权重。产品设计有个MVP原则,你可以了解下,道理是通用的。

    2020-03-17 07:22:52

  • 无名

    2020-05-13 00:06:52

    老师再来个V8源码解读的课程
  • mfist

    2020-03-16 18:52:23

    从去年看到v8课程的介绍,就准备购买了。从上次浏览器相关的课程收获挺大,希望老师这次课程能带我们了解v8 的内部机制。
    作者回复

    看到老朋友了

    2020-03-17 07:29:20

  • 刘大夫

    2020-03-23 12:20:38

    请教下前辈,为什么解释执行就会导致语言执行速度过慢呢,期待您简单回复,或者给出相关资料我自己去查也可以,感谢
    作者回复

    因为以下几个原因:
    执行过程中要先编译源码,再执行,多了个编译阶段

    大部分解释性语言是动态语言,动态语言的数据类型是在运行时检查的,这又耗费了额外的时间

    2020-03-25 20:33:40

  • 夜空中最亮的星

    2020-03-16 18:02:17

    老师我成功劝说一个前端买了,正在努力劝说第二个
    作者回复

    2020-03-17 07:27:24

  • 非洲大地我最凶

    2020-03-17 09:17:31

    看完浏览器课程来看这个,真的写的很好,老师我有一个问题,JS引擎和浏览器的进程之间有什么关系呢,是在渲染的时候调用JS引擎吗
    作者回复

    是的,每个渲染进程使用一份v8,渲染进程是v8当然宿主

    2020-03-17 22:03:03

  • 思不歌

    2020-04-05 22:32:13

    老师,有一个疑惑想问下,v8作为js解释器,是会将js代码转成c++执行吗,还是会直接编译成机器码,丢进内存执行。
    作者回复

    不是转换为C++,是转换为字节码,然后有个解释器来执行字节码!

    如果有一块字节码会被反复执行,那么V8会将字节码转换为二进制机器代码,然后cpu直接执行二进制的机器代码?

    2020-04-08 09:02:23

  • undefined

    2020-03-16 18:57:38

    插图是用什么工具画的?
    作者回复

    mac上的keynote

    2020-03-17 07:28:39

  • starj

    2020-03-20 10:28:02

    作为一个nodejs开发,不可解v8不行啊
  • NikkiZeng

    2020-03-16 19:48:35

    老师,v8的事件循环系统和浏览器的事件循环系统不是同一个东西么?
    作者回复

    是一套系统,浏览器有、NodeJS都分别有自己的他们的事件循环系统

    2020-03-17 07:31:47

  • 啊哈哈

    2020-12-14 15:19:12

    老师好,v8将js转成在字节码,通过解释器执行,对于反复执行的代码,会转成二进制码,最后由CPU执行,那么第一:解释器执行字节码不需要转成二进制码吗?第二:解释器执行的字节码不是在CPU执行?
  • 小疯子一头

    2020-03-18 07:51:22

    希望能保持浏览器专栏的水准 感谢
  • 不记年

    2020-03-17 12:59:35

    我不是做前端的,但是对编译原理和异步编程很感兴趣,所以这门课也不能错过~
    作者回复

    加油

    2020-03-17 22:04:45

  • 狂躁小胖

    2020-03-17 12:52:14

    浏览器工作原理看了两遍,浏览器不再是黑盒了,现在学V8,V8也不会再是黑盒了 !-_-!
  • 思不歌

    2020-05-09 11:26:42

    老师,v8执行js代码时,内存区域怎么分配,会和Jvm类似吗
    作者回复

    类似的,堆中有新生代,老生代,字节码区,机器吗区,栈区

    2020-05-23 09:39:24

  • 68cc

    2022-08-24 09:11:15

    老师就像您说的浏览器在持续进步,今年学的知识点明年可能就改动了,我们该如何知道最新的知识点?
  • 68cc

    2022-08-24 09:07:04

    老师您讲的很好,但是很多知识点感觉没有能落地的感觉。您可以讲下如何从源码的角度去理清一个知识点吗
  • Geek_f35fca

    2022-03-04 16:19:44

    老师,前端如何学习数据结构,有哪些书可以推荐
  • hao-kuai

    2021-12-16 07:20:10

    浏览器原理很早就买了,看了几篇然后束之高阁,最近拿出来看到收获良多,果断过来再收割一波
  • Geek_bbf24b

    2021-10-02 22:48:32

    老师,有深入讲解v8源码和浏览器源码的课程吗,求推荐