❝
人的本能就是对不太明白的东西敬而远之
❞
一语中的
- Chromium本身就是一个浏览器
- Chrome浏览器一般选择Chromium的稳定版本作为它的基础
- 浏览器大战,其实就是渲染引擎之争
- v8是「JS虚拟机」的一种
- 源代码对 V8 来说只是「一堆字符串」
- 执行JS代码核心流程 1. 先编译 2. 后执行
- V8采用「JIT」(Just In Time)技术提升效率
文章概要
- 浏览器简史:从群雄涿鹿到一家独大
- V8:一款高性能JS和WebAssembly引擎
- 执行JS代码的流程
在聊我们今天主角V8之前,我感觉有必要简单的把浏览器的发展史描述一下。「以史为镜,可以知兴替」
浏览器简史:从群雄涿鹿到一家独大
提到浏览器,Berners-Lee[1]是一个不得不提的人。Berners-Lee是W3C组织的理事,他在1990年发明了世界上第一个浏览器WorldWideWeb(后改名为Nexus)。
如果大家想试试第一款浏览器,那就体验一下哇[2] (自备🪜)
1993年,浏览器Mosaic[3]诞生,这就是后来的网景(Netscape)浏览器。在当时,网景浏览器大受欢迎,占据了绝大多数的市场份额。
受Mosaic浏览器的影响,微软于1995年推出了Internet Explorer(IE)浏览器,自此第一次浏览器大战正式打响。收益于Windows操作系统,IE逐渐取代了网景浏览器的领导地位。第一次浏览器大战以IE获胜收场。
处于低谷的网景公司于1998年成立了Mozilla基金会,在该基金会推动下,网景公司主导开发FireFox 浏览器。2004年发布1.0版本,拉开了第二次浏览器大战的序幕。
在FireFox浏览器发布1.0版本的前一年(2003年),苹果发布了Safari浏览器。并于2005年,苹果公司发起了一个新的开源项目 Webkit4(它是Safari浏览器的内核)。
2008年,Google公司以苹果开源项目Webkit作为内核,创建了一个新的项目 Chromium[5] (自备🪜)。在Chromium项目的基础上,Google发布了自己的浏览器Chrome。 「Chromium本身就是一个浏览器」,而不是Chrome浏览器内核,Chrome浏览器一般选择Chromium的稳定版本作为它的基础。Chromium是开源试验场。
自此,桌面浏览器形成了三足鼎立的局面 (IE/FireFox/Chrome)。
而随着时间的推移,Chrome浏览器在桌面领域一骑绝尘。将原来的对手抛到了脑后。根据statcounter[6]的最新统计,Chrome浏览器已经占据了半壁江山。
❝
浏览器大战,其实就是渲染引擎之争
❞
Chrome/Safari的渲染引擎,其实是一脉相承的。都是基于Webkit直接开发或者衍生出来的。
V8:一款高性能JS和WebAssembly引擎
用了一小段八股文描述了一下,浏览器的发展历史和现在浏览器的市场占有情况。发现Chrome以绝对的优势在浏览器市场称雄称霸。本着**「打不过,那就加入它」**的不抵抗方针,我们后面所有的文章,都用Chrome来讲解和实验。
时间不早了,我们干点正事哇。
V8是谷歌用C++编写的开源高性能JavaScript
和WebAssembly
引擎。它被用于Chrome和Node.js等。
(针对JS的介绍,可以参考之前写的JS篇之数据类型那些事儿[7],并且后期我们也会有针对该技术的介绍和分析。)
简单的说就是:
❝
v8是**「JS虚拟机」**的一种
(除了V8,还有其他类型的JS虚拟机。
例如Safari中的JavaScriptCore,FireFox中的TraceMonkey等)
❞
在CPU是如何运行程序的文章中介绍,程序(高级语言)如果被CPU识别和执行,就需要进行**「转换」**。而这个转换操作又根据语言特性分为:1. 解释执行 2. 编译执行。 昨天我们通过对一段C代码,进行分析、执行,了解了编译执行的过程。而针对解释执行,却没有举例说明。今天,让我们把这个坑给填上。
把 V8 看成是一个虚构出来的计算机,也称为**「虚拟机」。虚拟机通过模拟实际计算机的各种功能来实现代码的执行。如模拟实际计算机的 CPU、堆栈、寄存器等, 并且还有属于它自己的一套「指令系统」**。
可以简单的把JS虚拟机理解成一个**「翻译」**程序: 将人类能够理解的编程语言 JS,翻译成机器能够理解的机器语言。
执行JS代码的流程
准备工作
需要准备执行 JS 时所需要的一些基础环境
- 初始化了内存中的堆和栈结构
- JS全局执行上下文 (包含了执行过程中的全局信息, 比如一些内置函数,全局变量等信息)
- 全局作用域 (包含了一些全局变量, 在执行过程中的数据都需要存放在内存中)
- 「初始化消息循环系统」 (1. 消息驱动器 2. 消息队列)
执行流程
- V8 接收到要执行的 JS 源代码 (源代码对 V8 来说只是**「一堆字符串」**,V8 并不能直接理解这段字符串的含义)
- V8结构化这段字符串,生成了**「抽象语法树」** (AST),同时还会生成相关的**「作用域」**
- 生成字节码(介于 AST 和机器代码的中间代码)。与特定类型的机器代码无关
- 解释器(ignition),按照顺序解释执行字节码,并输出执行结果。
❝
从图中得出一个结论:
执行JS代码核心流程 1. 先编译 2. 后执行
❞
通过V8将js转换为字节码然后经过解释器执行输出结果的方式执行JS,有一个弊端就是,如果在浏览器中再次打开相同的页面,当页面中的 JavaScript 文件没有被修改,再次编译之后的二进制代码也会保持不变,意味着编译这一步**「浪费了 CPU 资源」**。
为了,更好的利用CPU资源,V8采用**「JIT」**(Just In Time)技术提升效率:而是混合编译执行和解释执行这两种手段。
❝
1.解释执行的启动速度快,但是执行时的速度慢
2.编译执行的启动速度慢,但是执行时的速度快
❞
为了能够实现编译执行,V8又引入了TurboFan(优化编译器),并且在解释执行字节码的过程中,如果发现了某一段代码会被**「重复多次执行」**,监控机器人就会将这段代码标记为热点代码。
当某段代码被标记为热点代码后,V8 就会将这段字节码丢给优化编译器(TurboFan),优化编译器会在后台将字节码编译为**「二进制代码」**,然后再对编译后的二进制代码执行优化操作,优化后的二进制机器代码的执行效率会得到大幅提升。
同时,由于JS是动态语言, 对象的结构和属性是可以在运行时任意修改的,经过优化编译器优化过的代码只能针对某种固定的结构。一旦在执行过程中,对象的结构被动态修改了, 优化之后的代码势必会变成无效的代码,优化编译器就需要执行**「反优化操作」**,经过反优化的代码,下次执行时就会回退到解释器解释执行。
参考资料:
- Webkit技术内幕
- Google V8
Reference
[1]
Berners-Lee: baike.baidu.com/item/%E8%92…
[2]
体验一下哇: worldwideweb.cern.ch/browser/#ht…
[3]
Mosaic: baike.baidu.com/item/Mosaic…
[4]
Webkit: webkit.org/
[5]
Chromium: www.chromium.org/
[6]
statcounter: gsa.statcounter.com/browser-mar…
[7]
JS篇之数据类型那些事儿: mp.weixin.qq.com/s/v-nqPc22f…