精华内容
下载资源
问答
  • 及时编译—之前的JS执行引擎需要先将源码转成字节码然后才能去执行,而对于V8来说可以直接将源码翻译成可执行的机器码 内存空间设置了数值,对于64位操作系统来说不超过1.5G,对于32位操作系统来说不超过800M—V8是...

    认识V8

    在这里插入图片描述

    • Chrome浏览器、node这样的平台都在采用V8去(高效)执行JS代码
    • 及时编译—之前的JS执行引擎需要先将源码转成字节码然后才能去执行,而对于V8来说可以直接将源码翻译成可执行的机器码
    • 内存空间设置了数值,对于64位操作系统来说不超过1.5G,对于32位操作系统来说不超过800M—V8是针对浏览器来使用的,对于现有的网页来说这样的大小足够用了;V8内部使用的垃圾回收机制决定了它使用这样的内存限制是合理的,官方做的测试:当垃圾内存达到1.5G时,如果V8采用增量标记算法回收只要消耗50ms,如果采用非增量标记算法回收要消耗1s,所以这里以1.5G为界了,给V8内存的内存做了1.5G的上限设置
    展开全文
  • 前端开发 V8引擎什么

    千次阅读 2019-03-31 18:52:48
    V8是Google的开源高性能JavaScript和WebAssembly引擎,用C ++编写。它用于Chrome和Node.js等。它实现ECMAScript和WebAssembly,并在Windows 7或更高版本,macOS 10.12+以及使用x64,IA-32,ARM或MIPS处理器的Linux...

            V8是Google的开源高性能JavaScript和WebAssembly引擎,用C ++编写。它用于Chrome和Node.js等。它实现ECMAScript和WebAssembly,并在Windows 7或更高版本,macOS 10.12+以及使用x64,IA-32,ARM或MIPS处理器的Linux系统上运行。 V8可以独立运行,也可以嵌入到任何C ++应用程序中。

    最新的博客文章:

    1. Blazingly fast parsing, part 1: optimizing the scanner 25 March 2019 internals parsing
    2. V8 release v7.4 22 March 2019 release
    3. JIT-less V8 13 March 2019 internals
    4. V8 release v7.3 07 February 2019 release
    5. Trash talk: the Orinoco garbage collector 03 January 2019 internals memory presentations
    6. V8 release v7.2 18 December 2018 release
    7. Speeding up spread elements 04 December 2018 ECMAScript benchmarks
    8. Faster async functions and promises 12 November 2018 ECMAScript benchmarkspresentations
    9. V8 release v7.1 31 October 2018 release
    10. V8 release v7.0 15 October 2018 release

    更多的文章如下

    Latest @v8js tweets

    View tweets by @v8js

     

    原文:https://v8.dev/

    展开全文
  • V8引擎详解(一)——概述

    千次阅读 2020-04-26 20:53:19
    在现有的javascript引擎中,V8引擎绝对是其中的佼佼者,chrome和node底层都使用了V8引擎,其中chrome的市场占有率已经达到70%,而node更是前端工程化以及扩展边界的核心支柱,V8引擎对于一个前端开发工程师来说重要...

    背景

    在现有的javascript引擎中,V8引擎绝对是其中的佼佼者,chrome和node底层都使用了V8引擎,其中chrome的市场占有率已经达到70%,而node更是前端工程化以及扩展边界的核心支柱,V8引擎对于一个前端开发工程师来说重要程度可想而知。我们大部分javascript开发者也许没有机会自己开发一个javascript引擎,但是学习V8引擎绝对是我们提升技术能力的重要途径,故专门查阅各种书籍和文章进行学习,整理出系列文章让我们一起学习进步。(本文为系列文章第一篇)

    什么是V8引擎

    javascript引擎

    了解V8引擎之前我们先要知道什么是javascript引擎。简单来说,CPU并不认识我们的js代码,而不同的CPU只认识自己对应的指令集,javascript引擎将js代码编译成CPU认识的指令集,当然除了编译之外还要负责执行以及内存的管理。
    大家都知道js是解释形语言,由引擎直接读取源码,一边编译一边执行,这样效率相对较低,而编译形语言(如c++)是把源码直接编译成可直接执行的代码执行效率更高。


    (图片转载自https://zhuanlan.zhihu.com/p/27628685)

    v8引擎

    随着技术的发展,对JavaScript性能的要求越来越高,这就更快速的解析和执行JavaScript代码,V8引擎就是在此背景下产生的,它产生的目的就是为了提高性能。
    为了提高性能v8向很多同时解释形语言的老前辈学习了很多经验,我们先来看一下同是解释形语言的java的运行过程。


    参考文章:
    什么是JIT:https://blog.csdn.net/qq_36042506/article/details/82976586

    我们再看一下V8是怎么做的


    整个过程和java的编译执行过程非常像,将javascript代码编译成抽象语法树再转化成字节码,通过解释器来执行,并通过JIT工具将部分字节码转化成可直接执行的本地代码。而java是分两个阶段完成,在编译阶段尽可能的生成高效的字节码。V8更加直接的将抽象语法树通过JIT技术转换成本地代码,放弃了在字节码阶段可以进行的一些性能优化,但保证了执行速度。虽然少了生成字节码这一阶段的性能优化,但极大减少了转换时间。

    V8编译运行过程

    接下来我们来了解一下v8的编译过程和运行过程

    编译过程

    首先我们要了解一下在执行编译运行过程中所用到的几个类

    • Script类:表示是JavaScript代码,既包含源代码,又包含编译之后生成的本地代码,所以它既是编译入口,又是运行入口;
    • Compiler类:编译器类,辅助Script类来编译生成代码,它主要起一个协调者的作用,会调用解释器(Parser)来生成抽象语法树和全代码生成器,来为抽象语法树生成本地代码;
    • Parser类:将源代码解释并构建成抽象语法树,使用AstNode类来创建它们,并使用Zone类来分配内存;
    • AstNode类:抽象语法树节点类,是其他所有节点的基类,它包含非常多的子类,后面会针对不同的子类生成不同的本地代码;
    • AstVisitor类:抽象语法树的访问者类,主要用来遍历抽象语法树;
    • FullCodeGenerator:AstVisitor类的子类,通过遍历抽象语法树来为JavaScrit生成本地代码;


    原始图片来源:《WebKit技术内幕》
    (红色标注为笔者自己理解,如果有偏差或者错误,欢迎指出和讨论)

    运行过程

    • Script表示JavaScript代码,即包含源代码,又包含编译之后生成的本地代码,即是编译入口,又是运行入口;
    • Execution:运行代码的辅组类,包含一些重要函数,如Call函数,它辅组进入和执行Script代码;
    • JSFunction:需要执行的JavaScript函数表示类;
    • Runtime:运行这些本地代码的辅组类,主要提供运行时所需的辅组函数,如:属性访问、类型转换、编译、算术、位操作、比较、正则表达式等;
    • Heap:运行本地代码需要使用的内存堆类;
    • MarkCompactCollector:垃圾回收机制的主要实现类,用来标记、清除和整理等基本的垃圾回收过程;
    • SweeperThread:负责垃圾回收的线程。

    执行过程如下:


    原始图片来源:《WebKit技术内幕》
    (红色标注为笔者自己理解,如果有偏差或者错误,欢迎指出和讨论)

    编译和执行的整体过程如下:


    原始图片来源:《WebKit技术内幕》
    (红色标注为笔者自己理解,如果有偏差或者错误,欢迎指出和讨论)

    总结

    本文以一个宏观的角度描述了V8引擎编辑和运行的过程,但是要想真正的了解V8除了这些,我们还需要了解很多里面的运行细节包括不限于 事件循环系统、内存管理、延迟解析、隐藏类、内联缓存等等,接下来的文章会逐一学习,敬请期待。

    参考文章

    《WebKit技术内幕》
    认识 V8 引擎: https://zhuanlan.zhihu.com/p/27628685

    展开全文
  • c# 、asp.net 在后台使用谷歌V8引擎执行js,将示例工程中的GoogleV8Engine.cs文件复制到你的项目中。将GoogleV8Engine_x64.dll 和 GoogleV8Engine_x86.dll 两个非托管DLL文件拷贝到工程部署的DLL目录下(ASP.Net拷贝...
  • 要开始进入v8 iOS运行时,请确保已安装XCode和 ,然后运行以下命令 # Clone repo git clone https://github.com/NativeScript/ns-v8ios-runtime.git # Install CMake and LLVM (8) brew install cmake llvm@8 # Open...
  • Chrome 的V8 引擎简介

    2019-08-24 11:34:22
    V8引擎就是为解决这一问题而生,在node中也是采用该引擎来解析JavaScript。V8是如何使得JavaScript性能有大幅提升的呢?通过对一些书籍和文章的学习,梳理了V8的相关内容,本文将带你认识 V8。  1.渲染引擎及网页...

     随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScript脚本。V8引擎就是为解决这一问题而生,在node中也是采用该引擎来解析JavaScript。V8是如何使得JavaScript性能有大幅提升的呢?通过对一些书籍和文章的学习,梳理了V8的相关内容,本文将带你认识 V8。

      1.渲染引擎及网页渲染

      浏览器自从上世纪80年代后期90年代初期诞生以来,已经得到了长足的发展,其功能也越来越丰富,包括网络、资源管理、网页浏览、多页面管理、插件和扩展、书签管理、历史记录管理、设置管理、下载管理、账户和同步、安全机制、隐私管理、外观主题、开发者工具等。在这些功能中,为用户提供网页浏览服务无疑是最重要的功能,下面将对相关内容进行介绍。

      1.1.渲染引擎

      渲染引擎:能够将HTML/CSS/JavaScript文本及相应的资源文件转换成图像结果。渲染引擎的主要作用是将资源文件转化为用户可见的结果。在浏览器的发展过程中,不同的厂商开发了不同的渲染引擎,如Tridend(IE)、Gecko(FF)、WebKit(Safari,Chrome,Andriod浏览器)等。WebKit是由苹果2005年发起的一个开源项目,引起了众多公司的重视,几年间被很多公司所采用,在移动端更占据了垄断地位。更有甚者,开发出了基于WebKit的支持HTML5的web操作系统(如:Chrome OS、Web OS)。

      下面是WebKit的大致结构:

      上图中实线框内模块是所有移植的共有部分,虚线框内不同的厂商可以自己实现。下面进行介绍:

    • 操作系统:是管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行。WebKit也是在操作系统上工作的。
    • 第三方库,为了WebKit提供支持,如图形库、网络库、视频库等。
    • WebCore 是各个浏览器使用的共享部分,包括HTML解析器、CSS解析器、DOM和SVG等。JavaScriptCore是WebKit的默认引擎,在谷歌系列产品中被替换为V8引擎。WebKit Ports是WebKit中的非共享部分,由于平台差异、第三方库和需求的不同等原因,不同的移植导致了WebKit不同版本行为不一致,它是不同浏览器性能和功能差异的关键部分。
    • WebKit嵌入式编程接口,供浏览器调用,与移植密切相关,不同的移植有不同的接口规范。
    • 测试用例,包括布局测试用例和性能测试用例,用来验证渲染结果的正确性。

      1.2.网页渲染流程

      上面介绍了渲染引擎的各个模块,那么一张网页,要经历怎样的过程,才能抵达用户面前?

      首先是网页内容,输入到HTML解析器,HTML解析器解析,然后构建DOM树,在这期间如果遇到JavaScript代码则交给JavaScript引擎处理;如果来自CSS解析器的样式信息,构建一个内部绘图模型。该模型由布局模块计算模型内部各个元素的位置和大小信息,最后由绘图模块完成从该模型到图像的绘制。在网页渲染的过程中,大致可分为下面3个阶段。

      1.2.1.从输入URL到生成DOM树

    1. 地址栏输入URL,WebKit调用资源加载器加载相应资源;
    2. 加载器依赖网络模块建立连接,发送请求并接收答复;
    3. WebKit接收各种网页或者资源数据,其中某些资源可能同步或异步获取;
    4. 网页交给HTML解析器转变为词语;
    5. 解释器根据词语构建节点,形成DOM树;
    6. 如果节点是JavaScript代码,调用JavaScript引擎解释并执行;
    7. JavaScript代码可能会修改DOM树结构;
    8. 如果节点依赖其他资源,如图片\css、视频等,调用资源加载器加载它们,但这些是异步加载的,不会阻碍当前DOM树继续创建;如果是JavaScript资源URL(没有标记异步方式),则需要停止当前DOM树创建,直到JavaScript加载并被JavaScript引擎执行后才继续DOM树的创建。

      1.2.2.从DOM树到构建WebKit绘图上下文

    1. CSS文件被CSS解释器解释成内部表示;
    2. CSS解释器完成工作后,在DOM树上附加样式信息,生成RenderObject树;
    3. RenderObject节点在创建的同时,WebKit会根据网页层次结构构建RenderLayer树,同时构建一个虚拟绘图上下文。

      1.2.3.绘图上下文到最终图像呈现

    1. 绘图上下文是一个与平台无关的抽象类,它将每个绘图操作桥接到不同的具体实现类,也就是绘图具体实现类;
    2. 绘图实现类也可能有简单的实现,也可能有复杂的实现,软件渲染、硬件渲染、合成渲染等;
    3. 绘图实现类将2D图形库或者3D图形库绘制结果保存,交给浏览器界面进行展示。

      上述是一个完整的渲染过程,现代网页很多都是动态的,随着网页与用户的交互,浏览器需要不断的重复渲染过程。

      

    1.3.JavaScript引擎

      JavaScript本质上是一种解释型语言,与编译型语言不同的是它需要一遍执行一边解析,而编译型语言在执行时已经完成编译,可直接执行,有更快的执行速度(如上图所示)。JavaScript代码是在浏览器端解析和执行的,如果需要时间太长,会影响用户体验。那么提高JavaScript的解析速度就是当务之急。JavaScript引擎和渲染引擎的关系如下图所示:

      JavaScript语言是解释型语言,为了提高性能,引入了Java虚拟机和C++编译器中的众多技术。现在JavaScript引擎的执行过程大致是:

      源代码-→抽象语法树-→字节码-→JIT-→本地代码(V8引擎没有中间字节码)。一段代码的抽象语法树示例如下:

    function demo(name) {
        console.log(name);
    }

      

     

    抽象语法树如下:


      

         V8更加直接的将抽象语法树通过JIT技术转换成本地代码,放弃了在字节码阶段可以进行的一些性能优化,但保证了执行速度。在V8生成本地代码后,也会通过Profiler采集一些信息,来优化本地代码。虽然,少了生成字节码这一阶段的性能优化,但极大减少了转换时间。

      但是在2017年4月底,v8 的 5.9 版本发布了,新增了一个 Ignition 字节码解释器,将默认启动,从此之后将与JSCore有大致相同的流程。做出这一改变的原因为:(主要动机)减轻机器码占用的内存空间,即牺牲时间换空间;提高代码的启动速度;对 v8 的代码进行重构,降低 v8 的代码复杂度(V8 Ignition:JS 引擎与字节码的不解之缘 - CNode技术社区)。

      JavaScript的性能和C相比还有不小的距离,可预见的未来估计也只能接近它,而不是与它相比,这从语言类型上已经决定。下面将对V8引擎进行更为细致的介绍。

      2.V8引擎

      V8引擎是一个JavaScript引擎实现,最初由一些语言方面专家设计,后被谷歌收购,随后谷歌对其进行了开源。V8使用C++开发,,在运行JavaScript之前,相比其它的JavaScript的引擎转换成字节码或解释执行,V8将其编译成原生机器码(IA-32, x86-64, ARM, or MIPS CPUs),并且使用了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序。V8支持众多操作系统,如windows、linux、android等,也支持其他硬件架构,如IA32,X64,ARM等,具有很好的可移植和跨平台特性。
      V8项目代码结构如下:

      2.1.数据表示

      JavaScript是一种无类型语言,在编译时并不能准确知道变量的类型,只可以在运行时确定,这就不像c++或者java等静态类型语言,在编译时候就可以确切知道变量的类型。然而,在运行时计算和决定类型,会严重影响语言性能,这也就是JavaScript运行效率比C++或者JAVA低很多的原因之一。

      在C++中,源代码需要经过编译才能执行,在生成本地代码的过程中,变量的地址和类型已经确定,运行本地代码时利用数组和位移就可以存取变量和方法的地址,不需要再进行额外的查找,几个机器指令即可完成,节省了确定类型和地址的时间。由于JavaScript是无类型语言,那就不能像c++那样在执行时已经知道变量的类型和地址,需要临时确定。JavaScript 和C++有以下几个区别:

    • 编译确定位置,C++编译阶段确定位置偏移信息,在执行时直接存取,JavaScript在执行阶段确定,而且执行期间可以修改对象属性;
    • 偏移信息共享,C++有类型定义,执行时不能动态改变,可共享偏移信息,JavaScript每个对象都是自描述,属性和位置偏移信息都包含在自身的结构中;
    • 偏移信息查找,C++查找偏移地址很简单,在编译代码阶段,对使用的某类型成员变量直接设置偏移位置,JavaScript中使用一个对象,需要通过属性名匹配才能找到相应的值,需要更多的操作。

      在代码执行过程中,变量的存取是非常普遍和频繁的,通过偏移量来存取,使用少数两个汇编指令就能完成,如果通过属性名匹配则需要更多的汇编指令,也需要更多的内存空间。示例如下:

      在JavaScript中,除boolean,number,string,null,undefined这个五个简单变量外,其他的数据都是对象,V8使用一种特殊的方式来表示它们,进而优化JavaScript的内部表示问题。

      在V8中,数据的内部表示由数据的实际内容和数据的句柄构成。数据的实际内容是变长的,类型也是不同的;句柄固定大小,包含指向数据的指针。这种设计可以方便V8进行垃圾回收和移动数据内容,如果直接使用指针的话就会出问题或者需要更大的开销,使用句柄的话,只需修改句柄中的指针即可,使用者使用的还是句柄,指针改动是对使用者透明的。

      除少数数据(如整型数据)由handle本身存储外,其他内容限于句柄大小和变长等原因,都存储在堆中。整数直接从value中取值,然后使用一个指针指向它,可以减少内存的占用并提高访问速度。一个句柄对象的大小是4字节(32位设备)或者8字节(64位设备),而在JavaScriptCore中,使用的8个字节表示句柄。在堆中存放的对象都是4字节对齐的,所以它们指针的后两位是不需要的,V8用这两位表示数据的类型,00为整数,01为其他。

      JavaScript对象在V8中的实现包含三个部分:隐藏类指针,这是v8为JavaScript对象创建的隐藏类;属性值表指针,指向该对象包含的属性值;元素表指针,指向该对象包含的属性。

      2.2.工作过程

      前面有过介绍,V8引擎在执行JavaScript的过程中,主要有两个阶段:编译和运行,与C++的执行前完全编译不同的是,JavaScript需要在用户使用时完成编译和执行。在V8中,JavaScript相关代码并非一下完成编译的,而是在某些代码需要执行时,才会进行编译,这就提高了响应时间,减少了时间开销。

    在V8引擎中,源代码先被解析器转变为抽象语法树(AST),然后使用JIT编译器的全代码生成器从AST直接生成本地可执行代码。这个过程不同于JAVA先生成字节码或中间表示,减少了AST到字节码的转换时间,提高了代码的执行速度。但由于缺少了转换为字节码这一中间过程,也就减少了优化代码的机会。

      V8引擎编译本地代码时使用的主要类如下所示:

    • Script:表示JavaScript代码,即包含源代码,又包含编译之后生成的本地代码,即是编译入口,又是运行入口;
    • Compiler:编译器类,辅组Script类来编译生成代码,调用解释器(Parser)来生成AST和全代码生成器,将AST转变为本地代码;
    • AstNode:抽象语法树节点类,是其他所有节点的基类,包含非常多的子类,后面会针对不同的子类生成不同的本地代码;
    • AstVisitor:抽象语法树的访问者类,主要用来遍历异构的抽象语法树;
    • FullCodeGenerator:AstVisitor类的子类,通过遍历AST来为JavaScript生成本地可执行代码。

     

     JavaScript代码编译的过程大致为:Script类调用Compiler类的Compile函数为其生成本地代码。Compile函数先使用Parser类生成AST,再使用FullCodeGenerator类来生成本地代码。本地代码与具体的硬件平台密切相关,FullCodeGenerator使用多个后端来生成与平台相匹配的本地汇编代码。由于FullCodeGenerator通过遍历AST来为每个节点生成相应的汇编代码,缺失了全局视图,节点之间的优化也就无从谈起。

      在执行编译之前,V8会构建众多全局对象并加载一些内置的库(如math库),来构建一个运行环境。而且在JavaScript源代码中,并非所有的函数都被编译生成本地代码,而是延迟编译,在调用时才会编译。

      由于V8缺少了生成中间代码这一环节,缺少了必要的优化,为了提升性能,V8会在生成本地代码后,使用数据分析器(profiler)采集一些信息,然后根据这些数据将本地代码进行优化,生成更高效的本地代码,这是一个逐步改进的过程。同时,当发现优化后代码的性能还不如未优化的代码,V8将退回原来的代码,也就是优化回滚。下面介绍一下运行阶段,该阶段使用的主要类如下所示:

    • Script:表示JavaScript代码,即包含源代码,又包含编译之后生成的本地代码,即是编译入口,又是运行入口;
    • Execution:运行代码的辅组类,包含一些重要函数,如Call函数,它辅组进入和执行Script代码;
    • JSFunction:需要执行的JavaScript函数表示类;
    • Runtime:运行这些本地代码的辅组类,主要提供运行时所需的辅组函数,如:属性访问、类型转换、编译、算术、位操作、比较、正则表达式等;
    • Heap:运行本地代码需要使用的内存堆类;
    • MarkCompactCollector:垃圾回收机制的主要实现类,用来标记、清除和整理等基本的垃圾回收过程;
    • SweeperThread:负责垃圾回收的线程。

      

    先根据需要编译和生成这些本地代码,也就是使用编译阶段那些类和操作。在V8中,函数是一个基本单位,当某个JavaScript函数被调用时,V8会查找该函数是否已经生成本地代码,如果已经生成,则直接调用该函数。否则,V8引擎会生成属于该函数的本地代码。这就节约了时间,减少了处理那些使用不到的代码的时间。其次,执行编译后的代码为JavaScript构建JS对象,这需要Runtime类来辅组创建对象,并需要从Heap类分配内存。再次,借助Runtime类中的辅组函数来完成一些功能,如属性访问等。最后,将不用的空间进行标记清除和垃圾回收。

      2.3.优化回滚

      因为V8是基于AST直接生成本地代码,没有经过中间表示层的优化,所以本地代码尚未经过很好的优化。于是,在2010年,V8引入了新的编译器-Crankshaft,它主要针对热点函数进行优化,基于JavaScript源代码开始分析而非本地代码,同时构建Hydroger图并基于此来进行优化分析。

      Crankshaft编译器为了性能考虑,通常会做出比较乐观和大胆的预测—代码稳定且变量类型不变,所以可以生成高效的本地代码。但是,鉴于JavaScript的一个弱类型的语言,变量类型也可能在执行的过程中进行改变,鉴于这种情况,V8会将该编译器做的想当然的优化进行回滚,称为优化回滚。

      示例如下:

    var counter = 0;
    function test(x, y) {
        counter++;
        if (counter < 1000000) {
            // do something
            return 'jeri';
        }
        var unknown = new Date();
        console.log(unknown);
    }

      该函数被调用多次之后,V8引擎可能会触发Crankshaft编译器对其进行优化,而优化代码认为示例代码的类型信息都已经被确定。但,由于尚未真正执行到new Date()这个地方,并未获取unknown这个变量的类型,V8只得将该部分代码进行回滚。优化回滚是一个很耗时的操作,在写代码过程中,尽量不要触发优化该操作。

      在最近发布的 V8 5.9 版本中,新增了一个 Ignition 字节码解释器,TurboFan 和 Ignition 结合起来共同完成JavaScript的编译。这个版本中消除 Cranshaft 这个旧的编译器,并让新的 Turbofan 直接从字节码来优化代码,并当需要进行反优化的时候直接反优化到字节码,而不需要再考虑 JS 源代码。

      2.4.隐藏类与内嵌缓存

      2.4.1.隐藏类

      在执行C++代码时,仅凭几个指令即可根据偏移信息获取变量信息,而JavaScript里需要通过字符串匹配来查找属性值的,这就需要更多的操作才能访问到变量信息,而代码量变量存取是十分频繁的,这也就制约了JavaScript的性能。V8借用了类和偏移位置的思想,将本来通过属性名匹配来访问属性值的方法进行了改进,使用类似C++编译器的偏移位置机制来实现,这就是隐藏类。

      隐藏类将对象划分成不同的组,对于组内对象拥有相同的属性名和属性值的情况,将这些组的属性名和对应的偏移位置保存在一个隐藏类中,组内所有对象共享该信息。同时,也可以识别属性不同的对象。示例如下:
      使用Point构造了两个对象p和q,这两个对象具有相同的属性名,V8将它们归为同一个组,也就是隐藏类,这些属性在隐藏类中有相同的偏移值,p和q共享这一信息,进行属性访问时,只需根据隐藏类的偏移值即可。由于JavaScript是动态类型语言,在执行时可以更改变量的类型,如果上述代码执行之后,执行q.z=2,那么p和q将不再被认为是一个组,q将是一个新的隐藏类。

      2.4.2.内嵌缓存

      正常访问对象属性的过程是:首先获取隐藏类的地址,然后根据属性名查找偏移值,然后计算该属性的地址。虽然相比以往在整个执行环境中查找减小了很大的工作量,但依然比较耗时。能不能将之前查询的结果缓存起来,供再次访问呢?当然是可行的,这就是内嵌缓存。

      内嵌缓存的大致思路就是将初次查找的隐藏类和偏移值保存起来,当下次查找的时候,先比较当前对象是否是之前的隐藏类,如果是的话,直接使用之前的缓存结果,减少再次查找表的时间。当然,如果一个对象有多个属性,那么缓存失误的概率就会提高,因为某个属性的类型变化之后,对象的隐藏类也会变化,就与之前的缓存不一致,需要重新使用以前的方式查找哈希表。

      2.5.内存管理

      Node中通过JavaScript使用内存时就会发现只能使用部分内存(64位系统下约为1.4 GB,32位系统下约为0.7 GB),其深层原因是 V8 垃圾回收机制的限制所致(如果可使用内存太大,V8在进行垃圾回收时需耗费更多的资源和时间,严重影响JS的执行效率)。下面对内存管理进行介绍。

      内存的管理组要由分配和回收两个部分构成。V8的内存划分如下:

    • Zone:管理小块内存。其先自己申请一块内存,然后管理和分配一些小内存,当一块小内存被分配之后,不能被Zone回收,只能一次性回收Zone分配的所有小内存。当一个过程需要很多内存,Zone将需要分配大量的内存,却又不能及时回收,会导致内存不足情况。
    • 堆:管理JavaScript使用的数据、生成的代码、哈希表等。为方便实现垃圾回收,堆被分为三个部分:

      年轻分代:为新创建的对象分配内存空间,经常需要进行垃圾回收。为方便年轻分代中的内容回收,可再将年轻分代分为两半,一半用来分配,另一半在回收时负责将之前还需要保留的对象复制过来。
      年老分代:根据需要将年老的对象、指针、代码等数据保存起来,较少地进行垃圾回收。
      大对象:为那些需要使用较多内存对象分配内存,当然同样可能包含数据和代码等分配的内存,一个页面只分配一个对象。

      

    垃圾回收

      V8 使用了分代和大数据的内存分配,在回收内存时使用精简整理的算法标记未引用的对象,然后消除没有标记的对象,最后整理和压缩那些还未保存的对象,即可完成垃圾回收。

      在V8中,使用较多的是年轻分代和年老分代。年轻分代中的对象垃圾回收主要通过Scavenge算法进行垃圾回收。在Scavenge的具体实现中,主要采用了Cheney算法:通过复制的方式实现的垃圾回收算法。它将堆内存分为两个 semispace,一个处于使用中(From空间),另一个处于闲置状态(To空间)。当分配对象时,先是在From空间中进行分配。当开始进行垃圾回收时,会检查From空间中的存活对象,这些存活对象将被复制到To空间中,而非存活对象占用的空间将会被释放。完成复制后,From空间和To空间的角色发生对换。在垃圾回收的过程中,就是通过将存活对象在两个 semispace 空间之间进行复制。年轻分代中的对象有机会晋升为年老分代,条件主要有两个:一个是对象是否经历过Scavenge回收,一个是To空间的内存占用比超过限制。

      对于年老分代中的对象,由于存活对象占较大比重,再采用上面的方式会有两个问题:一个是存活对象较多,复制存活对象的效率将会很低;另一个问题依然是浪费一半空间的问题。为此,V8在年老分代中主要采用了Mark-Sweep(标记清除)标记清除和Mark-Compact(标记整理)相结合的方式进行垃圾回收。

      2.6.快照

      在V8引擎启动时,需要构建JavaScript运行环境,需要加载很多内置对象,同时也需要建立内置的函数,如Array,String,Math等。为了使V8更加整洁,加载对象和建立函数等任务都是使用JavaScript文件来实现的,V8引擎负责提供机制来支持,就是在编译和执行JavaScript前先加载这些文件。

      V8引擎需要编译和执行这些内置的JavaScript代码,同时使用堆等来保存执行过程中创建的对象、代码等,这些都需要时间。为此,V8引入了快照机制。将这些内置的对象和函数加载之后的内存保存并序列化。序列化之后的结果很容易反序列化,经过快照机制的启动时间可以缩减几毫秒。快照机制也可以将一些开发者认为需要的JavaScript文件序列化,以减少处理时间。不过快照机制的加载的代码不能被CrankShaft这样的编译器优化,可能会存在性能问题。

      3.V8 VS JavaScriptCore

      JavaScriptCore引擎是WebKit中默认的JavaScript引擎,也是苹果开源的一个项目,应用较为广泛。最初,性能不是很好,从2008年开始了一系列的优化,重新实现了编译器和字节码解释器,使得引擎的性能有较大的提升。随后内嵌缓存、基于正则表达式的JIT、简单的JIT及字节码解释器等技术引入进来,JavaScriptCore引擎也在不断的迭代和发展。

      V8引擎自诞生之日起就以性能优化作为目标,引入了众多新技术,极大了带动了整个业界JavaScript引擎性能的快速发展。总的来说,V8引擎较为激进,青睐可以提高性能的新技术,而JavaScriptCore引擎较为稳健,渐进式的改变着自己的性能。总的来说JavaScript引擎工作流程(包含v8和JavaScriptCore)如下所示:
      JavaScriptCore 的大致流程为:源代码-→抽象语法树-→字节码-→JIT-→本地代码。JavaScriptCore与V8有一些不同之处,其中最大的不同就是新增了字节码的中间表示,并加入了多层JIT编译器(如:简单JIT编译器、DFG JIT编译器、LLVM等)优化性能,不停的对本地代码进行优化。(在 V8 的 5.9 版本中,新增了一个 Ignition 字节码解释器,TurboFan 和 Ignition 结合起来共同完成JavaScript的编译,此后 V8 将与 JavaScriptCore 有大致相同的流程,Node 8.0中 V8 版本为 5.8)

      还有就是在数据表示方面,V8在不同的机器上使用与机器位数相匹配的数据表示,而在JavaScriptCore中句柄都是使用64位表示,其可以表示更大范围的数字,所以即使在32位机器上,浮点类型同样可以保存在句柄中,不再需要访问堆中的数据,当也会占用更多的空间。

      4.功能扩展

      JavaScript引擎的主要功能是解析和执行JavaScript代码,往往不能满足使用者多样化的需要,那么就可以增加扩展以提升它的能力。V8引擎有两种扩展机制:绑定和扩展。

      4.1.绑定机制

      使用IDL文件或接口文件生成绑定文件,将这些文件同V8引擎一起编译。WebKit中使用IDL来定义JavaScript,但又与IDL有所不同,有一些改变。定义一个新的接口的步骤大致如下:

    • 1.定义新的接口文件,可以在JavaScript代码进行调用,如mymodule.MyObj.myAttr;
    module mymodule {
        interface [
                InterfaceName = MyObject
        ] MyObj { 
            readonly attribute long myAttr;
            DOMString myMethod (DOMString myArg);
        };
    }
    • 2.按照引擎定义的标准接口为基础实现接口类,生成JavaScript引擎所需的绑定文件。WebKit提供了工具帮助生成所需的绑定类,根据引擎不同和引擎开发语言的不同而有所差异。V8引擎会为上述示例代码生成 v8MyObj.h (MyObj类具体的实现代码)和 V8MyObj.cpp (桥接代码,辅组注册桥接的函数到V8引擎)两个绑定文件。

      JavaScript引擎绑定机制需要将扩展代码和JavaScript引擎一块编译和打包,不能根据需要在引擎启动后再动态注入这些本地代码。在实际WEB开发中,开发者都是基于现有浏览器的,根本不可能介入到JavaScript引擎的编译中,绑定机制有很大的局限性,但其非常高效,适用于对性能要求较高的场景。

      4.2. Extension机制

      通过V8的基类Extension进行能力扩展,无需和V8引擎一起编译,可以动态为引擎增加功能特性,具有很大的灵活性。

      Extension机制的大致思路就是,V8提供一个基类Extension和一个全局注册函数,要想扩展JavaScript能力,需要经过以下步骤:

    class MYExtension : public v8::Extension {
        public:
            MYExtension() : v8::Extension("v8/My", "native function my();") {}
            virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction (
            v8::Handle<v8::String> name) {
                // 可以根据name来返回不同的函数
                return v8::FunctionTemplate::New(MYExtention::MY);
            }
            static v8::Handle<v8::Value> MY(const v8::Arguments& args) {
                // Do sth here
                return v8::Undefined();
            }
    };
    MYExtension extension;
    RegisterExtension(&extension);
    • 1.基于Extension基类构建一个它的子类,并实现它的虚函数—GetNativeFunction,根据参数name来决定返回实函数;
    • 2.创建一个该子类的对象,并通过注册函数将该对象注册到V8引擎,当JavaScript调用’my’函数时就可被调用到。

      Extension机制是调用V8的接口注入新函数,动态扩展非常方便,但没有绑定机制高效,适用于对性能要求不高的场景。

      总结

      在过去几年,JavaScript在很多领域得到了广泛的应用,然而限于JavaScript语言本身的不足,执行效率不高。Google也推出了一些JavaScript网络应用,如Gmail、Google Maps及Google Docs office等。这些应用的性能不仅受到服务器、网络、渲染引擎以及其他诸多因素的影响,同时也受到JavaScript本身执行速度的影响。然而既有的JavaScript引擎无法满足新的需求,而性能不佳一直是网络应用开发者最关心的。Google就开始了V8引擎的研究,将一系列新技术引入JavaScript引擎中,大大提高了JavaScript的执行效率。相信随着V8引擎的不断发展,JavaScript也会有更广泛的应用场景,前端工程师也会有更好的未来!
    那么结合上面对于V8引擎的介绍,我们在编程中应注意:

    • 类型。对于函数,JavaScript是一种动态类型语言,JavaScriptCore和V8都使用隐藏类和内嵌缓存来提高性能,为了保证缓存命中率,一个函数应该使用较少的数据类型;对于数组,应尽量存放相同类型的数据,这样就可以通过偏移位置来访问。
    • 数据表示。简单类型数据(如整型)直接保存在句柄中,可以减少寻址时间和内存占用,如果可以使用整数表示的,尽量不要用浮点类型。
    • 内存。虽然JavaScript语言会自己进行垃圾回收,但我们也应尽量做到及时回收不用的内存,对不再使用的对象设置为null或使用delete方法来删除(使用delete方法删除会触发隐藏类新建,需要更多的额外操作)。
    • 优化回滚。在执行多次之后,不要出现修改对象类型的语句,尽量不要触发优化回滚,否则会大幅度降低代码的性能。
    • 新机制。使用JavaScript引擎或者渲染引擎提供的新机制和新接口提高性能。

      参考资料

    展开全文
  • Chrome V8引擎介绍

    千次阅读 2018-09-28 15:53:45
    V8引擎就是为解决这一问题而生,在node中也是采用该引擎来解析JavaScript。V8是如何使得JavaScript性能有大幅提升的呢?通过对一些书籍和文章的学习,梳理了V8的相关内容,本文将带你认识 V8。 1.渲染引擎及网页...
  • google v8引擎 x64静态库

    2019-11-29 11:12:57
    v8版本8.0.397 win10下vs2019编译的x64静态库 debug版本/MDd release版本/MD
  • chrome_v8_js引擎源码导读分析 第一章 v8 之整体流程 第二章 v8 之全局环境配置及初始化 2.1 全局模板 2.2 库函数 2.3 初始化 第三章 v8 之前端建立语法树 3.1 v8 编译中重要的类 3.2 compile之前的查找 ...
  • js中v8引擎的详解-看的吐血

    万次阅读 多人点赞 2019-05-25 23:16:19
    v8引擎出现的原因 这里先说一下什么是编译型语言和解释性语言: 编译型语言: 在程序执行之前必须进行专门的编译过程,有如下特点: 只须编译一次就可以把源代码编译成机器语言,后面的执行无须重新编译,直接使用...
  • 最新版Google V8引擎库,谷歌Chrome JavaScript引擎,VS2013 32位编译,可正式使用
  • 编写Python API,然后使用V8引擎从JavaScript中调用它们。使用V8引擎从JavaScript中调用Python API
  • VS2019谷歌V8引擎8.2版本,编译好的Release版DLL和LIB,和测试demo 下载即可使用。 window版本 已经包含include,文件齐全。
  • Go的V8 JavaScript引擎绑定。 特征 线程安全 彻底仔细的测试 布尔值,数字,字符串,对象,数组,正则表达式,函数 编译并运行JavaScript 保存并加载预编译的脚本数据 使用全局对象模板创建JavaScript上下文 在Go...
  • delphi调用v8引擎.zip

    2019-07-09 09:33:06
    delphi调用v8引擎.zip
  • 使用Visual Studio 2013编译V8引擎 代码如下: 准备工作,安装Python2.x,git,svn: Git: http://msysgit.github.io SVN:http://www.sliksvn.com/en/download Python:https://www.python.org/downloads/ 第一步,...
  • 认识V8引擎 v8是js执行的引擎(nodeJs和谷歌浏览器采用的引擎) v8采用即时编译(将源码编译成可直接执行的机械码,不需要转字节码操作) v8内存设限(32位不超过800M,64不超1.5G),增量标记只需50毫秒,非增量需...
  • 下面将对V8引擎进行更为细致的介绍 V8引擎 V8引擎是一个JavaScript引擎实现,最初由一些语言方面专家设计,后被谷歌收购,随后谷歌对其进行了开源。V8使用C++开发,在运行JavaScript之前,相比其它的JavaScript的...
  • delphi 源码 调用 google v8引擎 高效率执行js代码 最新版本 运行正常
  • V8引擎样例

    2017-08-01 17:51:34
    v8引擎代码样例
  • V8 Javascript引擎库(libv8)主 ) V8是Google的开源Javascript引擎V8用C ++编写,并在Google Chrome(谷歌的开源浏览器)中使用。 V8实现了ECMA-262,第5版中指定的ECMAScript。 此扩展程序要求V8 6.9(已知...
  • 在android上编译v8
  • Google V8 引擎

    万次阅读 多人点赞 2017-07-11 20:06:22
    V8的前世今生V8是JavaScript渲染引擎,第一个版本随着Chrome的发布而发布(具体时间为2008年9月2日)。在运行JavaScript之前,相比其它的JavaScript的引擎转换成字节码或解释执行,V8将其编译成原生机器码(IA-32, x86...
  • 编写Python API,然后使用V8引擎从JavaScript调用它们。 >> > from v8py import Context >> > context = Context () >> > def print_hello (): ... print 'Hello, world!' >> > context . expose ( print_hello ) ...
  • v8引擎详解

    千次阅读 2018-10-23 19:19:51
     前面有过介绍,V8引擎在执行JavaScript的过程中,主要有两个阶段:编译和运行,与C++的执行前完全编译不同的是,JavaScript需要在用户使用时完成编译和执行。在V8中,JavaScript相关代码并非一下完成编译的,而是...
  • Google V8 引擎文档

    热门讨论 2011-11-19 00:03:34
    Google V8脚本解释引擎文档 学习笔记,很实用
  • 问题抛出:JavaScript代码在浏览器内是如何被执行的呢?(下一篇解答) 本文做知识的铺垫,有助于对原理的理解。 浏览器输入url后的大致...浏览器的内核又叫排版引擎,浏览器引擎,渲染引擎 浏览器的渲染过程 浏览.
  • V8引擎简介

    2018-12-09 14:14:27
    本篇文章将会深入到谷歌V8 JavaScript引擎的内核部分。我们也会提供一些怎样写出更好的JavaScript代码的建议。 概览 一个JavaScript引擎是执行JavaScript代码的一个程序或者说一个解释器。一个JavaScript引擎可以...
  • 这个是windows编译google v8的方式,,里面是txt文档说明,可以百分百编译成功,支持任何版本的vc。静态库和动态库都可以,,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 31,973
精华内容 12,789
关键字:

v8引擎是什么