WebKit作为一个浏览器引擎,其中Javascript实现包括JavaScriptCore和V8,为了能更全面的了解WebKit,我们需要深入的了解Javascript实现的基本原理、其在WebKit中的作用以及与其他部分之间的交互,同时与Gecko中的Javacript实现作初步的对比。让我们开始了解WebKit之Javascript实现JavaScriptCore、V8之旅吧。

什么是javascript?

javascript是一门动态、弱类型、基于原型的脚本语言,其核心部分包含对脚本的编译、解析、执行、反编译、垃圾回收及标准类/类型如 bool、string、number的实现等;然后在此基础通过将已经符合DOM标准的文档DOM接口按照javascript接口定义及声明的方式绑定/导出到 javascript运行环境中,以丰富javascript的内涵,其本质相当于有了java虚拟机后,通过import不同类型的类库来扩展 java语言的应用。

目前javascript比较独特的地方在于它自身没有一个完整独立的运行环境,其往往依附于浏览器,由浏览器来提供运行环境,并控制或发起javascript进行编译、解析执行脚本、垃圾回收等,其核心部分相当于一个符合ECMAScript标准的动态库,以供浏览器来调用,这样看来其本质是为了对浏览器主要部分的扩展及更灵活运用的支持,从MVC的角度来看,javascript相当于浏览器中的控制部分,其应用场景往往具有一定的局限性、独特性的;而通用的脚本语言如perl、python、 ruby等都提供了完整独立的语言运行环境及线程管理等,并且提供了良好的扩展机制以丰富语言的内涵。

为什么JavaScript在现代引擎(V8,JavaScriptCore)性能卓越

下面测试下斐波拉契递归

console.time('a')
function fs(n) {
  if (n <= 2) {
    return 1;
  } else {
    return fs(n - 1) + fs(n - 2);
  }
}
console.log(fs(15))
console.timeEnd('a')

分别使用Java,JavaScript,PHP,Ruby这四门语言编写了脚本,计算n=40的兔子数列,速度如下

  1. C或者C++的代码我没有写,肯定跑得比狗还快。

  2. 首先是Java,编译出字节码耗时约1s,运行字节码耗时约1s,666。

  3. 其次是JavaScript,在node环境下运行耗时约3.5s,在浏览器环境(Safari)下约8s,66。

  4. 接着是Ruby,出人意料的结果,约39s,6不起来了。

  5. 最后是PHP,约80s,233。

产生性能差异的原因:

静态类型vs动态类型

概括来说就是,静态类型语言在编译后会大量利用类型已知的优势,比如int类型,占用4个字节,编译后的代码就可以使用内存地址加偏移量的方法存取变量。而地址+偏移量的算法汇编非常容易实现。

那动态类型语言是如何做的呢?概括的来说就是当做字符串通通存下来,之后存取就用字符串匹配。

编译型vs解释性

编译型语言,就像C/C++,代码要经过编译器编译成可执行程序后才可以运行。这个编译过程没什么时间要求,所以编译器可以做大量代码优化措施,有时候编译要好久好久。

解释型语言,就像JavaScript,就是引擎直接读源码,然后就出结果,当然这样子做效率非常低。就像靠人脑去读源码,然后写答案一样。

奇葩型语言,就像Java,有编译过程,但编译产出的是中间代码(字节码),这个过程也有充分的时间做优化。也有解释过程,字节码需要由Java虚拟机解释执行。

从这儿,大概可以理解,为什么C/C++运行效率比Java更高。因为不管怎么说,直接运行二进制码都比解释执行字节码来得快吧。

所以,有趣的事情就来了,C/C++是大哥,Java是二哥,一群解释型脚本语言是小弟们。

现代JavaScript引擎的努力

Java想的肯定是优化虚拟机解释执行字节码的速度,这儿正是和大哥拉开差距的地方。从大哥那学了很多招。其中重要的一招就是JIT(Just-In-Time),主要的思想就是解释器在解释字节码的时候,会将部分字节码转化成本地代码(汇编代码),这样可以被CPU直接执行,而不是解释执行,从而极大地提高性能。

JavaScript从前辈那里学习了很多,总结来说有:

  • 优化数据表示,弥补动态类型的性能缺陷

  • 引入一个编译过程,而不是直接解释执行,但这个编译过程和运行是一起的,时间的权衡变得非常重要。

  • JIT技术,与Java中的JIT原理相同

V8引擎与JavaScriptCore引擎

各个JavaScript优化的具体实现不太一样。

  • V8引擎对于编译和JIT的做法是,在编译阶段的过程是:源码=》抽象语法树=》本地代码。其中从抽象语法树到本地代码的过程使用的是JIT全码生成器,其作用是将抽象语法树转换成各个硬件平台和直接运行的本地代码。V8引擎的这种思路看起来像想要越过二哥Java,直接学大哥C/C++啊。

  • JavaScriptCore引擎的做法是,在编译阶段的过程是:源码=》抽象语法树=》字节码(中间代码)。对这个阶段像极了二哥Java的编译过程,只是这里小弟可没有充裕的时间做优化。于是大量的字节码优化措施被延后,比如JIT。JavaScriptCore引擎使用DFG JIT、LLVM等继续对字节码做优化。

WebKit中的Javascript实现

WebKit中其Javascript实现,同样相当于一个符合ECMAScript标准的动态库,其往往依附于浏览器引擎,由浏览器引擎来提供运行环境,并控制或发起javascript实现进行编译、解析执行脚本、垃圾回收等,同样需提供对浏览器引擎扩展的支持如Dom Binding等;

由于Web2.0的提出,动态网页的交互如运行ajax更加的频繁,Javascript脚本运行的总体效率以及安全往往成为浏览器内核的关键,而其Javascript实现就担负着如此重任。

JavaScriptCore实现特点

相对于其他的Javascript实现,JavaScriptCore提出了虚拟机的概念,在编译脚本时生成高效的bytecode,bytecode统一在一个虚拟机的环境中执行。而其高效的虚拟机实现常称为SquirrelFish,通过Announcing SquirrelFishIntroducing SquirrelFish Extreme可更进一步了解关于SquirrelFish的相关内容。

V8实现特点

Fast Property Access

To reduce the time required to access JavaScript properties, V8 does not use dynamic lookup to access properties. Instead, V8 dynamically creates hidden classes  behind the scenes. This basic idea is not new - the prototype-based programming language Self used maps to do something similar. (See for example, An Efficient Implementation of Self, a Dynamically-Typed Object-Oriented Language Based on Prototypes). In V8, an object changes its hidden class when a new property is added.

Dynamic Machine Code Generation

V8 compiles JavaScript source code directly into machine code when it is first executed.  There are no intermediate byte codes, no interpreter. Property access  is handled by inline cache code that may be patched with other machine instructions as V8 executes.

During initial execution of the code for accessing a property of a given object, V8 determines the object's current hidden class. V8 optimizes property access by predicting that this class will also be used for all future objects accessed in the same section of code and uses the information in the class to patch the inline cache code to use the hidden class. If V8 has predicted correctly the property's value is assigned (or fetched) in a single operation. If the prediction is incorrect, V8 patches the code to remove the optimisation.

Efficient Garbage Collection

V8   reclaims memory used by  objects  that are no longer required in a process known as garbage collection. To ensure fast object allocation, short garbage collection pauses, and no memory fragmentation V8 employs a stop-the-world, generational, accurate, garbage collector. This means that V8:

  • stops  program execution when performing a garbage collection cycle.

  • processes only part of the object heap in most garbage collection cycles. This minimizes the impact of stopping the application.

  • always knows exactly where all  objects and pointers are in memory. This avoids falsely identifying objects as pointers which  can result in memory leaks.

In V8, the object heap is segmented into two parts: new space where objects are created, and old space to which objects surviving a garbage collection cycle are promoted. If an object is moved in a garbage collection cycle, V8 updates all pointers to the object.

JavaScriptCore、V8如何与WebCore交互

在WebCore::Frame的数据结构中包含数据成员KJSProxy* m_jscript;而在Chrome的代码中调整为JSBridge* m_jscript;而针对不同实现JavaScriptCore、V8,分别有:

class KJSBridge : public JSBridge {
    public:
    KJSBridge(Frame* frame) : m_proxy(new KJSProxy(frame)) { }
    virtual ~KJSBridge() { delete m_proxy; }
    ........................
    private:
    KJSProxy* m_proxy;
};
class V8Bridge : public JSBridge {
    public:
    explicit V8Bridge(Frame* frame);
    virtual ~V8Bridge();
    .......................
    private:
    V8Proxy* m_proxy;
};
V8Bridge::V8Bridge(Frame* frame) {
    m_proxy = new V8Proxy(frame);
}
V8Bridge::~V8Bridge() {
    delete m_proxy;
}

而不同的KJSProxy与V8Proxy分别对应不同的Javascript实现,它们分别实现了与WebCore之间的共同接口,其主要数据结构分别如下:

class V8Proxy {
    Frame* m_frame;
    v8::Persistent<v8::context> m_context;
    v8::Persistent<v8::object> m_global;
    // Special handling of document wrapper;
    v8::Persistent m_document;
    int m_handlerLineno;
    ...........................
};

具体不同Javascript实现如何实现与WebCore的接口,需了解不同Javascript实现逻辑;

如对Javascript实现逻辑及基本原理感兴趣,可具体参考其提供的api及sample等等;

至于Dom Binding的实现,JavaScriptCore与V8通过通过同样的方式来实现,可参考《浅谈WebKit之WebCore篇》中所描述的Javascript实现如何与WebCore集成等;

具体Dom Binding的实现可参考generate-bindings.pl生成的代码,其中的内容一定会让你受益非浅,同时为将Javascript实现嵌入到其他应用中去提供非常有益的参考。如对window的实现,特别是open方法的实现,很值得研究研究。。。

初步对比JavaScriptCore、V8、SpiderMonkey等

具体JavaScriptCore、V8、SpiderMonkey、TracMonkey执行效率对比如何,不同的测试方法会有不同的测试结果,在这里不再阐述。

就个人了解而言,觉得JavaScriptCore关于对象的方法、属性的安全访问控制方面略有欠缺;

SpiderMonkey作为最早一批实现Javascript的引擎,其代码使用C语言来实现,稍现复杂,没有象后来的实现如JavaScriptCore、V8等借鉴了最新的虚拟机技术如JVM等;

V8作为新近推出的Javascript实现,正与其特点所描述,拥有很多优势,同时基于C++实现,充分利用了C++ template,代码相对简洁,便于学习使用;

这方面安利下:

参考文章:

转载本站文章《WebKit三件套(2):WebKit之JavaScriptCore/V8》,
请注明出处:https://www.zhoulujun.cn/html/webfront/browser/webkit/2021_0421_8631.html

WebKit三件套(2):WebKit之JavaScriptCore/V8的更多相关文章

  1. [WebKit] JavaScriptCore解析--基础篇 (一)JSC与WebCore

    先看一下官方的基本介绍,短短几句就塞满了关键字. SquirrelFish,正式名称是JavaScriptCore,包括register-based(基于寄存器的虚拟机), direct-thread ...

  2. 【转】WebKit 与 V8 的关系

    页面的绘制(绘制,就是把一个HTML文件变成一个活灵活现的页面展示的过程...),只有一半轮子是Chrome自己做的,还有一部分来自于WebKit,这个Apple打造的Web渲染器...之所以说是一半 ...

  3. [转]开发者需要了解的WebKit(mark)

    以下内容转自:http://www.infoq.com/cn/articles/webkit-for-developers -------------------------------------- ...

  4. 八大Webkit内核浏览器

    列举出时下最流行的Webkit内核浏览器,所以我们并不会做出评测和对比.PS:本文列举的浏览器有一部分为IE+Webkit双核浏览器,如果您对其他IE内核浏览器很感兴趣<抛弃数据!用体验和感觉告 ...

  5. 开发者应当了解的WebKit知识

    开发者应当了解的WebKit知识 对一些开发者而言,WebKit就是一个黑盒子.丢进去HTML.CSS.JS等一连串的东西,而WebKit就能变魔术一般显示出一个很棒的网页出来.实际上,正我的同事Il ...

  6. 开发者需要了解的WebKit

    2013-3-22 22:37| 发布者: sxwgf| 查看: 575| 评论: 0|来自: infoq 摘要: Paul Irish是著名的前端开发工程师,同时他也是Chrome开发者关系团队成员 ...

  7. WebKit HTML、CSS、JS

    开发者需要了解的WebKit https://www.infoq.cn/article/webkit-for-developers 开发者需要了解的 WebKit   彭超 2013 年 3 月 18 ...

  8. How Chromium Displays Web Pages: Bottom-to-top overview of how WebKit is embedded in Chromium

    How Chromium Displays Web Pages This document describes how web pages are displayed in Chromium from ...

  9. 《Webkit技术内幕》之页面渲染过程

    文章同步到github<Webkit技术内幕>之页面渲染过程 最近拜读了传说中的<Webkit技术内幕>一书,有很大收获,尤其是对页面渲染有了较深的认识.由于功力有限,而且书中 ...

  10. About Webkit

    http://blog.csdn.net/spacetiller/article/details/5784461 一 . WebKit 简介 Webkit 是一个开放源代码的浏览器引擎 (web br ...

随机推荐

  1. .net core中你的MD5用对了吗?

    本文的项目环境为 .net 6.0 (.net 5.0 以上都支持) 在 .net 中获取字符串的 MD5 相信是非常容易的事情吧, 但是随便在网上搜一搜发现流传的版本还不少呢,比如: StringB ...

  2. Atcoder Regular Contest 166

    只打了半场. A. Replace C or Swap AB 首先如果存在某个 \(i\),使得 \(Y_i\) 是 C 且 \(X_i\) 不是,那么显然是不合法的,可以直接判掉. 那么除去上述情况 ...

  3. HttpWebResponse获取DOM数据注意之ContentEncoding

    public string GetKaiJ(string type = "ssq") { /*1.通过WebBrowser控件&HttpWebRequest获得网站信息*/ ...

  4. HTML DOM之二:事件

    对事件作出反应 当事件发生时,可以执行 JavaScript,比如当用户点击一个 HTML 元素时. 如需在用户点击某个元素时执行代码,请把 JavaScript 代码添加到 HTML 事件属性中: ...

  5. 【pwn】[SWPUCTF 2022 新生赛]InfoPrinter--格式化字符串漏洞,got表劫持,data段修改

    下载附件,checksec检查程序保护情况: No RELRO,说明got表可修改 接下来看主程序: 函数逻辑还是比较简单,14行出现格式化字符串漏洞,配合pwntools的fmtstr_payloa ...

  6. Spring Cloud 整合

    前言 玩SpringCloud之前最好懂SpringBoot,别搞撑死骆驼的事.Servlet整一下变成Spring:SSM封装.加入东西就变为SpringBoot:SpringBoot再封装.加入东 ...

  7. Tomcat 配合虚拟线程,一种新的编程体验

    Java 21 在今年早些时候的 9 月 19 日就正式发布,并开始正式引入虚拟线程,但是作为 Java 开发生态中老大哥 Spring 并没有立即跟进,而是在等待了两个月后的 11 月 29 日,伴 ...

  8. 关于win11系统修改用户名导致登录进入不了系统的坑

    背景:公司的新电脑,win11系统,开机进入需要注册用户名和密码,在取用户名的时候,手快没注意取了一个中文名,结果这给我后面的工作带来了一个坑,我在用mysqlworkbench进行数据备份,需要对数 ...

  9. drf实战和源码剖析----学习笔记1

    学自:bilibili武沛齐老师. 武老师讲课:清晰,连贯,实用,透彻,真乃名师! # 1. 什么是前后端分离 - 不分离,主要用于后台系统(CRUD)和用户量上的情况,开发起来代价小- 分离,面向用 ...

  10. crictl命令

    containerd提供了ctr命令行用于镜像管理容器,但功能比较简单 所以一般会用k8s提供的crictl命令. 该命令的特点是:只要符合K8S的CRI接口的,都可以使用. 另外一点就是,cricr ...