QQ2013中使用Webkit内核替换原IE内核,内核中使用V8作为JS引擎,使得JS执行性能有了极大的提升,本文主要分析了一下V8引擎的实现与优势。
V8引擎作为一种动态语言运行时平台,需要实现动态语言源程序解析、执行,基本流程如下:
其中各部分非固定不变,主要有以下模式:
- 虚拟机模式:编译成AST或字节码后,执行环境提供AST或字节码的执行。
- 本地代码模式:直接把字节码翻译成机器码,由CPU进行执行,类似静态语言。
- 混合模式:部分代码翻译成机器码,同时需运行时提供一些语言级别的动态访问能力。
其中虚拟机模式较为常用,实现简单且移于移植,但执行速度较慢;本地代码模式执行速度较快,对动态语言的支持复杂度较大;混合模式介于两者之间,将常用代码编译成机器码以提高执行速度。
用于提供动态语言特性支持的运行时环境,主要需要实现以下内部分内容:
- 对象模型,用于实现语言级别的泛型支持。
- 变量存储、访问、传递、回收。
- 调度控制,实现模拟CPU实现的语句调度等。
二.V8的设计
2.1 对象模型
对于JavaScript这类动态语言,不同于静态语言的确定性,动态语言中对象在运行时不仅类型可变,属性也可改变、增减:
var m = new object();
m=123;//先是一个数字
m[“a”]=234;//然后是一个map
m[1]=”abc”;
m[“2″][“b”]=”abc”;//然后是一个tree
故运行时环境需提供动态检测对象类型的功能,同时可动态增删、访问属性或数组元素(对应于数据结构中的map与array)。
先来看两种经典的泛型实现:
(1)全托管类型
全托管类型将对象分为类型与值两部分,值中托管了所有可能的对象类型。
以下是腾讯广研公共组件TData的实现:
classTData
{
TEDataTypeemDataType;//数据类型
void* pData;//指向数据的万能指针
}
其中pData为托管的指针,指向存储的值,托管了所有基本类型以及数据结构,其中Array的实现与map相同:
TData托管的类型 | 实际的存储类型 |
布尔值 | bool |
浮点数 | double |
整数 | int64 |
字符串型 | std::string |
Map | std::map<string,TData *> |
Array | 同上 |
NULL | 无 |
(2) 部分托管
部分托管模型将值与属性、数组元素分离,简单模型如下:
typedefunion
{
void* p;
int n;
} Value;
typedefstruct
{
int t;
Value v;
map<string, TObject*> properties; //访问属性
TObject** elements; //访问array元素
} TObject;
其中Value是个可扩展union,如lua中对应实现如下:
typedefunion
{
GCObject* gc;
void* p;
lua_Number n;
intb;
} Value;
若使用map来实现数组,则elements与properties可合并。
全托管类型全部使用堆分配,实现较为简单,但需分配两次内存;部分托管类型部分变量在栈分配(实际上还是堆),访问int等基本类型较快,同时明确区分值与属性、元素,可分别进行优化。
V8引擎的实现类似于部分托管类型,但接管了内存分配与管理,除了Smi(Small Integer),其他对象均在堆上分配。V8中所有对象均使用4字节(32位机)表示(伪码,非实际实现
typedef union
{
int32SmiValue;
void*p;
} Object;
32位内存的末位为0时表示Smi,为1时表示指针,从而区分Smi与堆上分配的对象。整体继承关系如下图(仅列出部分对象):
所有JS对象继承自JSObject,JS函数继承自JSProxy。
JSObject的内存分布如下:
MapPointer(32 bits) | PropertiesPointer(32 bits) | ElementsPointer(32 bits) |
其中MapPointer继承自HeapObject,指明对象类型、占用内存大小以及GC需要的一些信息,PropertiesPointer对应于属性访问的map,ElementsPointer对应于Array中的元素访问(即key为整数的属性访问)。可以看出JSObject的实现与部分托管相同,但不同于其中map与array的实现,V8中对属性及元素的访问进行了优化,见下节分析。
2.2 快速属性访问
JavaScript中不明确区分数组元素与属性,数组元素可看做以整数为key的map,v8中把两者分开处理,以提高array的访问速度。
(1)属性访问
先来看段代码:
function Point(x, y)
{
this.x = x;
this.y = y;
}
var point = new Point(2,3);
point.x = 4;
point.y = 5;
对于C++等静态语言,以上后两行代码可看成:
set [point+xOffset] 5
set [point+yOffset] 5
其中xOffset与yOffset为预先计算出来的常量,属性操作只需直接访问对应内存。对于JS这种动态语言无法实现这样的操作,因为创建对象后属性可动态增减,不可能预先计算出属性对应偏移值。
因此,大部分动态语言运行时的设计均是采用两种方式实现:
- map实现:以属性为key,红黑树查找,复杂度为O(logN)
- HashTable实现:通过属性计算hash值,复杂度为O(1)
显然HashTable复杂度优于map,但根据字符串计算hash值效率不高,且需要处理冲突问题,实际并非最优。以上两方式的实现同时需要在每个对象中存放各个属性名称,内存占用上也存在较大优化空间。
V8中针对属性的访问类似于HashTable的实现,但特别进行了以下优化:
1. 编译期,相同的属性名称字符串存放于堆上相同位置,将string转化为地址。
2. 引入隐藏类存放该类型中各属性的相对偏移值,针对属性个数的多少分别采用二分查找与线性查找的方式获取偏移值。
3. 引入以隐藏类+属性为key, 属性偏移值为value的Cache,提高重复访问属性时响应速度。
采用隐藏类实现快速访问,对象模型可简化表示为:
classTypeObject
{
int type;
intslotCount;
HashMap*slotMap;
};
class Object
{
TypeObject* map;
Object* properties;
Object* elements;
}
其中map中的slotMap即V8官网上所指的隐藏类,指明了每个属性对应的偏移值,properties与elements分别存放以string为key与以int为key的属性,可看成数组。如对以上的Point类,隐藏类如下:
以上实现模拟静态语言,虽然编译时对象没有类信息,但运行时动态生成类信息(称为隐藏类),隐藏类中存放各属性偏移值,且具有相同结构的对象指向相同隐藏类(隐藏类的实现详见官网https://developers.google.com/v8/design)。
对于访问对象属性的以下语句:
point.x=5
实际访问过程可简化为:
int index = SearchCache(point->slotMap, x);
//搜索Cache
if(index != NotFind)
{
SetProperty(point, index, 5);
return;
}
//搜索隐藏类
index = point->slotMap.Search(hash);
if(index != NotFind)
{
SetProperty(point, index, 5);
return;
}
//更新隐藏类
UpdateHiddenClass(point->slotMap, x);
index = point->NextFreePropertyIndex();
SetProperty(point, index, 5);
//更新Cache
UpdateCache(point->slotMap, x, index);
V8编译JS时将字符串”x”转化为对应StringObject,因此后续计算hash的操作仅需操作32位整数,同时使用了内联Cache及隐藏类确保属性的快速访问。
(2) array访问
array访问相对简单些,当出现过的最大下标较小且空间足够时,采用数组形式实现,否则采用HashTable方式实现。采用数组形式时,下标直接为数组下标,可直接访问,采用HashTable方式时,用下标计算Hash值,需要增加个求模操作,两者复杂度均为O(1)。对属性、数组元素访问源代码如下:
var array = new Object();
array.prop = 2; //属性方式,设置在properties中
array[3] = 3; //数组方式,设置在elements中
array[3000]=4;//原数组转化为HashTable,以下标为key,设置在elements中
综上,V8中对属性、数组元素访问流程如下:
2.3 变量管理
变量管理是运行时环境开发的难点,其中涉及变量访问、作用域控制、生命期管理三大部分内容。
传统运行时采用map保存变量,采用作用域链的方式控制变量访问及生命期管理,但对于引用类型的生命期管理无法完美解决,因此引入GC进行生命期控制。
V8中除Smi所有变量均在堆上分配,使用32bit的地址表示变量,由GC控制变量生命期。每个作用域中申请一个HandleScope,后续申请的变量均使用一个Handle持有该变量,并保存Handle到HandleScope中,当超出HandleScope作用域时,销毁HandleScope并释放其中所有Handle,由GC根据堆上变量是否被引用来决定变量的回收。
回收算法为分代回收,新申请的小对象放入Young Generational,未被回收对象放入Old Generational,针对不同时机分别采用Scavenge、Full non-compacting collection、Full compacting collection三种算法进行垃圾回收。
2.4 调度控制
基于虚拟机模式的调度控制通过模拟CPU的方式实现,包括取指令、目标跳转、参数压栈等,通常采用一个大switch实现字节码的执行与目标跳转。
而V8是采用动态代码生成,直接将树状的AST编译成线形的机器码,由CPU直接执行,因此省去指令执行与调度方面难点,并提高执行速度。
三. V8为什么快
相对于别的JS引擎,V8中主要引入下面几方面的优化以提高执行速度:
1. 编译优化:实现string转32bit地址,减小关于string的hash计算或搜索时间。
2. 属性访问优化:与常规hash方法相同,但引入隐藏类归纳相同结构减小内存占用。同时采用隐藏类-属性的内存Cache,实现属性快速访问。
3. 执行优化:采用动态代码生成,执行时减小字节码解析与执行的时间。
4. Cache优化:对代码、属性访问等引入Cache,提高重复执行代码的执行效率。
5. 垃圾回收机制:统一管理内存,减小作用域切换时频繁的内存分配与释放开销。
四. 总结
V8引擎采用的优化特性大大提高JS代码循环语句、属性访问、重复调用等方面的执行速度。
相对于原IE内核,V8引擎在JS执行速度上有极大提升,以下为各项JS执行性能测试的对比结果:
目前V8引擎的缺点是内存占用较大,需要保留各类Cache空间,GC机制也会预留约16M的空间,还需要进行优化。
相关推荐
一、V8引擎简介 V8引擎以其高效的即时编译(JIT)技术闻名,它将JavaScript代码直接编译成机器码,从而实现了快速执行。此外,V8引擎还采用了诸多现代优化技术,如垃圾回收机制、内联缓存和优化编译等,确保了代码...
1. **V8引擎简介**:了解V8的设计目标,即快速执行JavaScript代码,以及它是如何通过即时编译(JIT)和优化技术实现高性能的。 2. **V8的内存管理**:探讨V8的垃圾回收机制,包括分代垃圾回收、标记-清除算法、压缩...
V8 引擎简介。Google V8 引擎使用 C++ 代码编写,实现了 ECMAScript 规范的第五版,可以运行在所有的主流操作系统中,甚至可以运行在移动终端 ( 基于 ARM 的处理器,如 HTC G7 等 )。V8 最早被开发用以嵌入到 Google...
"delphi调用v8引擎.zip"这个压缩包文件很可能是提供了一个示例或教程,展示了如何在Delphi项目中集成和使用V8引擎来执行JavaScript代码。 集成V8引擎到Delphi应用程序中,开发者可以实现以下关键知识点: 1. **V8...
本篇文章将深入探讨如何在Delphi项目中调用Google V8引擎,实现高效率执行JavaScript代码。 首先,我们需要了解V8引擎的基本原理。V8引擎使用即时编译(JIT)技术将JavaScript代码转换为机器码,这使得它能以接近...
#### V8引擎简介 V8 是由 Google 开发的一款高性能的 JavaScript 引擎,主要用于 Chrome 浏览器和 Node.js 运行环境中。V8 的主要特点在于其将 JavaScript 直接编译为机器码而非中间字节码,从而大大提高了执行效率...
1. `v8_base.lib`: 这是V8引擎的基础库,包含V8的基本功能,如执行JavaScript代码、内存管理和垃圾回收。 2. `icui18n.lib` 和 `icuuc.lib`: 这两个库来自于ICU (International Components for Unicode),V8 使用...
V8引擎是Google开发的一款高性能JavaScript和WebAssembly的开源虚拟机。它被广泛应用于Chrome浏览器以及Node.js等项目,以其高效的执行速度和优秀的内存管理机制而著名。在本压缩包中,我们关注的是V8引擎的x64静态...
V8引擎是Google开发的一款高性能JavaScript和WebAssembly的开源运行时环境,以其高效、快速而闻名。它被广泛应用于Chrome浏览器以及Node.js等服务器端环境中。在VS2019中,开发者可以利用V8引擎来创建自己的...
V8引擎是Google开发的一款高性能的JavaScript和WebAssembly运行时环境,它被广泛应用于Chrome浏览器以及Node.js等服务器端JavaScript平台。V8引擎以其高效、快速的解释和编译JavaScript代码而闻名,它实现了...
首先,我们要了解V8引擎。V8是由Google开发的高性能JavaScript和WebAssembly虚拟机,用于Chrome和Node.js等项目。它实现了ECMAScript规范,提供了快速的代码解析、编译和执行。V8Py就是Python与V8引擎之间的桥梁。 ...
V8引擎是由Google开发的一款高性能JavaScript和WebAssembly的开源运行时环境。它是Chrome浏览器的核心组成部分,也常被用于服务器端的JavaScript应用。在本资源中,我们关注的是V8引擎的8.2版本,该版本是针对Visual...
谷歌的V8引擎是一款开源的JavaScript执行环境,它被广泛应用于Chrome浏览器和其他基于Chromium的项目。V8 version 3.14.0.1是该引擎的一个特定版本,发布于2012年左右。这个版本包含了V8引擎的核心功能和优化,使...
本文将深入探讨如何使用Visual Studio 2010(VS2010)来编译V8引擎,并将其嵌入到C++项目中。同时,我们还将介绍一个名为"TestJS"的Demo代码,帮助你更好地理解和应用编译后的V8库。 首先,编译V8引擎需要以下几个...
c# 、asp.net 在后台使用谷歌V8引擎执行js,将示例工程中的GoogleV8Engine.cs文件复制到你的项目中。将GoogleV8Engine_x64.dll 和 GoogleV8Engine_x86.dll 两个非托管DLL文件拷贝到工程部署的DLL目录下(ASP.Net拷贝...
谷歌V8引擎是一款由Google开发的高性能JavaScript和WebAssembly虚拟机,它被广泛应用于Chrome浏览器以及Node.js等服务器端环境。V8引擎以其快速的执行速度和高效的内存管理而著称,它通过即时编译(JIT)技术将...
V8引擎是Google开发的一款高性能的JavaScript和WebAssembly执行引擎,它是开源的,并且是Google Chrome浏览器以及Node.js等项目的基石。V8引擎以其高效的编译技术、即时编译(JIT)和优化策略,为JavaScript应用程序...
V8引擎是Google开发的一款高性能JavaScript和WebAssembly的开源虚拟机。它被广泛应用于Chrome浏览器以及Node.js服务器端运行环境中,为JavaScript代码提供强大的执行能力。这篇文章将详细讲解如何编译V8引擎,并探讨...
V8引擎是Google开发的一款高性能的JavaScript和WebAssembly虚拟机,它是开源的,并且被广泛应用于Chrome浏览器以及Node.js服务器环境中。V8引擎以其卓越的执行速度和对现代JavaScript特性的支持而闻名,使得Web应用...
V8引擎是Google开发的一款高性能JavaScript和WebAssembly的开源虚拟机。它被广泛应用于Chrome浏览器以及Node.js等服务器端环境,以提供快速的JavaScript执行能力。2014年11月的V8版本,虽然相对现在较旧,但仍然包含...