Javascript的V8引擎研究
1.针对上下文的Snapshot技术
什么是上下文(Contexts)?实际是JS应用程序的运行环境,避免应用程序的修改相互影响,例如一个页面js修改内置对象方法toString,不应该影响到另外页面。chrome浏览器每个process只有一个V8引擎实例,浏览器中的每个窗口、iframe都对应一个上下文。
V8启动时(在执行client js前),需要对全局上下文(第一个context)初始化,读取和解析自实现的内置JS代码(另一种技术,第2点),建立起function、array、string等内置对象及方法(参见bootstrapper中的Genesis类);后续context的创建只需要创建内置对象即可;为了减少全局上下文创建时的CPU、内存消耗,V8使用了Snapshot技术(参见v8_mksnapshot工程),(1)全局上下文初始化后,将目前堆内存序列化为字节代码,保存至磁盘文件;这个过程最重要的是空间地址和对象保存,具体操作(参见serialize文件)是:模拟线性内存空间的分配,遍历堆内存中的所有JS对象,并在模拟空间分配内存(实际是记录对象相对偏移地址),然后序列化对象大小、偏移地址、子对象、对象内容;最后是序列化global handler和stack上的context;(2)加载时,将该snapshot文件反序列化进内存,避免第一个上下文初始化,从而加快V8的启动。
2. Built-in的js代码
(1)利用JS自表达内置对象、方法,如上面代码实现Math.min方法,从而V8在实现代码转译时只需注重基本操作,以%符号开头的函数来自V8运行时函数(参见runtime和codegen);
(2)如何把js嵌入到V8中?利用python工具,将JS文件代码转为const char[]变量(字节代码),存到natives.cc;在执行期间首次访问相关方法时,取相关js代码进行lazy-compile。
(题外话:让面试者实现JS的一些常用函数,也是考察其基本功方法之一。)
3. 建立AST(Abstract SyntaxTree)时内存的管理
V8在建立AST后,对其进行汇编生成动态机器语言,所以AST在code generated后需要回收;针对AST建立过程中多结点内存申请和一次性回收的特点,V8使用了内存段链表管理,并结合scopelock模式,实现少数申请(Segment,8KB~1MB)、多次分配AST结点、一次回收各个Segment的管理方式,既能避免内存碎片,又可以避免遍历AST结点逐个回收内存。
4. CompileCache避免相同代码重复编译
对于一段JS代码,在开始进行词法分析前,会从编译缓存区CompilationCache查找该段代码是否已经被编译过,如果是,则直接取出编译过的机器代码,并返回,这样降低CPU的使用率,换来内存空间一定的占用;如果一个页面中重复加载JS文件,这方法的提速是很明显的;这种做法应该有平衡对比过。
下面先来看下这个缓存区的具体实现:
(1)数据结构采用CompilationCacheTableàHashTable,它为三种不同的JS语句类型(普通script,Eval语句,RegExp正则表达式),并使用了不太雅观的全局变量记录各种table。用三种table记录的原因:相同的script和Eval语句,编译出来的机器代码是不同的(来自annotation),因为Eval语句和执行期的上下文相关,所以缓存的时候还需要加上context作为key,所以三种类型生成的key方式不一样。一开始table数组的各个值为空,直到有编译代码需要加入时才生成hashtable。
(2)Key的生成,需要保证相同语句生成的key是稳定的:
a)普通script语句,使用StringHasher生成key,并保存到String对象中(因为该key不只是用到该处);生成规则是:当语句长度超过16K时,将直接使用它的长度作为key(经过偏移变为32位长度并确保为奇数),否则使用Bob Jenkins hash算法逐个读取语句的每个字符,计算组成一个uint的key;
b)Eval语句,同样使用到StringHasher,在这基础上结合上下文的share info(同样是语句hash,再加上语句的位置信息,详见StringSharedHashHelper)来更新hash值;
c)RegExp语句,同样使用到StringHasher,在这基础上结合RegExp的flag信息(i/g等)来更新hash值(详见RegExpObjectHash),具体见RegExpKey。
这几种key都派生自HashTableKey,后者定义了些公共接口,供hashtable统一使用。
(3)查找方式:使用hash算法,并用线性侦测来解决key的冲突,这是由HashTable继承自FixedArray的特性决定。
5. 属性的快速访问
C++、Java等语言有着类的概念,且属性、方法和类绑定在一起,访问时可根据对象地址+位移快速获得;而JS对象并没有类概念,它实际为hash map,属性可以动态增加、删除,而且在执行时才能获知对象类型。
V8没有像其它JS Engine使用词典结构或红黑树实现的map来管理属性,而是在每个对象附加一个指针,指向hidden class(如果第一次创建该类型对象,则新建hidden class);当对象每添加一个属性时,将新建一个class(记录了每个属性的位移/位置),而原来的class指向新class,即建立起一个hidden class的转换链表。
6. Heap堆内存管理
详见:
http://hi.baidu.com/hycjk/blog/item/20c9ecf87d3d1004d8f9fd6e.html
http://hi.baidu.com/hycjk/blog/item/86b2bf0e000d34ec37d1221e.html
7. Inline caching减少函数调用开销
详见http://en.wikipedia.org/wiki/Inline_caching。JS函数绑定发生在运行时,所以无法通过method tables定位函数入口;通过该技术可以记录函数入口,避免重复查找。
8. 一次性编译生成机器语言
一般JS engine会在AST生成后,将之编译为中间语言(bytecode),在执行时候再解析这些bytecode;Java 也同样编译为这些bytecode,再采用VM(实现跨平台)作为解释器,为了提高效能,Java采用混杂方式,把无关平台、常用的代码编译为机器代码。V8则是一次性把AST编译为机器语言。从assembler相关文件头的Copyright可以看出,这些不同平台(ia32, arm)下的编译器,原型来自Sun Microsystems。
附:
一、一些JS engine设计的考虑点:
1、 快速——解析、建构语法树、执行等多个方面,例如属性访问,避免字典查找;优化代码,编译生成动态机器码,而非按语句解释执行,且机器码可以存放cache后重复执行;
2、 小巧——占用内存低,分配回收内存及时和有效;
3、 安全——运行上下文切换和检查;
4、 容错性高
5、 易于与浏览器集成;
6、 跨平台;
7、 提供api接口;
8、 支持调试;
9、 其它细节:正则表达式的解析和实现、hash对象的实现(大多此类对象size较小),需要考虑JS本身特性和目的。
二、一些JS语言特性
1、对象为hash map,属性可以随时添加、修改、删除,对象类型在执行时才可知;
2、prototyp chain在执行时可以修改
3、eval能改变执行上下文
4、with动态添加对象到scope chain(作用域)
三、词法分析器
四、V8引用的第三方代码:
1、正则引擎:刚开始使用webkit项目中的JSCRE(基于剑桥大学Philip Hazel开发的,被广泛使用的PCRE库),后来替换为全新的Irregexp(Automata理论);
2、dtoa,double转为字符串,David M. Gay,under an MIT license;
3、Strongtalk assembler,是个以 C++ 封裝的 JIT (Just-In-Time) Assemble。
五、一些资料地址
http://code.google.com/intl/zh-CN/apis/v8/design.html
http://code.google.com/intl/zh-CN/apis/v8/embed.html
http://www.docin.com/p-46635034.html
http://www.greenpublishers.com/neat/200901/3coverstory.pdf
http://www.cnblogs.com/RicCC/archive/2008/02/15/javascript-object-model-execution-model.html
http://www.cnblogs.com/duguguiyu/archive/2008/10/02/1303095.html tagged pointer?
http://hllvm.group.javaeye.com/group/topic/17840
Javascript的V8引擎研究的更多相关文章
- Node.js和Chrome V8 引擎了解
说起Node就不得不先介绍一个Chrome V8 引擎. 随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScr ...
- JavaScript深入浅出第4课:V8引擎是如何工作的?
摘要: 性能彪悍的V8引擎. <JavaScript深入浅出>系列: JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼? JavaScript深入浅出第2课:函数是一等 ...
- JavaScript引擎研究与C、C++与互调用(转)
本文转自:ice6015的专栏.为什么有些招聘需要熟悉JS和C++,这或许就是原因. 1. 概要 JavaScript是一种广泛用于Web客户端开发的脚本语言,常用来控制浏览器的DOM树,给HTML ...
- JavaScript工作机制:V8 引擎内部机制及如何编写优化代码的5个诀窍
概述 JavaScript引擎是一个执行JavaScript代码的程序或解释器.JavaScript引擎可以被实现为标准解释器,或者实现为以某种形式将JavaScript编译为字节码的即时编译器. 下 ...
- 使用 D8 分析 javascript 如何被 V8 引擎优化的
在上一篇文章中我们讲了如何使用 GN 编译 V8 源码,文章最后编译完成的可执行文件并不是 V8,而是 D8.这篇我们讲一下如何使用 D8 调试 javascript 代码. 如果没有 d8,可以使用 ...
- How Javascript works (Javascript工作原理) (二) 引擎,运行时,如何在 V8 引擎中书写最优代码的 5 条小技巧
个人总结: 一个Javascript引擎由一个标准解释程序,或者即时编译器来实现. 解释器(Interpreter): 解释一行,执行一行. 编译器(Compiler): 全部编译成机器码,统一执行. ...
- v8引擎详解(摘)-- V8引擎是一个JavaScript引擎实现
随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScript脚本.V8引擎就是为解决这一问题而生,在node中也 ...
- JavaScript是如何工作的02:深入V8引擎&编写优化代码的5个技巧
概述 JavaScript引擎是执行 JavaScript 代码的程序或解释器.JavaScript引擎可以实现为标准解释器,或者以某种形式将JavaScript编译为字节码的即时编译器. 以为实现J ...
- JavaScript(二)——在 V8 引擎中书写最优代码
概述 一个 JavaScript 引擎就是一个程序或者一个解释程序,它运行 JavaScript 代码.一个 JavaScript 引擎可以用标准解释程序或者即时编译器来实现,即时编译器即以某种形式把 ...
随机推荐
- 关于css伪类
p:nth-child(2){} !选择所有p元素的第二个子元素: p:nth-of-type(2) !选择所有p元素第二个为p的子元素(是选择第二个类型为p的元素 而不是第二个子集为p的元素)
- mysql 函数示例(转)
MySQL函数大全及用法示例 1.字符串函数ascii(str) 返回字符串str的第一个字符的ascii值(str是空串时返回0) mysql> select ascii('2'); ...
- validate效验规则
] } }, messages:{ name:{ required:"最少为2个字!" }, tel:{ required:"请填写手机号码!", isMobi ...
- .net core2.0入门使用EF
使用Nuget导入所需要的EF 核心包以及对应数据库的驱动包,我用的是sqlserver(.net 支持的所有数据库) Install-Package Microsoft.EntityFramewor ...
- LINQ to SQL和Entity Framework
LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术. 那么为什么会有LINQ这个东西的出现呢. 简单来说LINQ是为了满足不知道怎么操作数据库的程序员开 ...
- BZOJ3309 DZY Loves Math(莫比乌斯反演+线性筛)
一通正常的莫比乌斯反演后,我们只需要求出g(n)=Σf(d)*μ(n/d)的前缀和就好了. 考虑怎么求g(n).当然是打表啊.设n=∏piai,n/d=∏pibi .显然若存在bi>1则这个d没 ...
- c语言基本数据类型(short、int、long、char、float、double)
一 C 语言包含的数据类型 short.int.long.char.float.double 这六个关键字代表C 语言里的六种基本数据类型. 在不同的系统上,这些类型占据的字节长度是不同的: 在32 ...
- java实现函数指针
在Java程序员面试笔试宝典中看到的东西,感觉很有用,特开此文 函数指针一般作为函数的参数来使用,开发人员在使用是可以根据自己的需求传递自动以的函数来实现指定的功能,例如,在实现排序算法时,可以通过传 ...
- 【Cf #503 C】Sergey's problem(有趣的构造)
感觉这种构造题好妙啊,可我就是想不到诶. 给出一张无自环的有向图,回答一个独立集,使得图中任意一点都可以被独立集中的某一点两步之内走到. 具体构造方案如下: 下标从小到大枚举点,如果该点没有任何标记, ...
- APK反编译之二:工具介绍
前面一节我们说过,修改APK最终是通过修改smali来实现的,所以我们接下来介绍的工具就是如何把APK中的smali文件获取出来,当然同时也需要得到AndroidManifest.xml等文件.直接修 ...