首页 > 编程语言 >JS引擎(1):JS引擎擂台赛,JavaScript引擎的特征比较及术语科普

JS引擎(1):JS引擎擂台赛,JavaScript引擎的特征比较及术语科普

时间:2023-04-09 18:24:04浏览次数:59  
标签:对象 JavaScript JS 编译 编译器 引擎 GC 擂台赛

上篇介绍过JavaScript引擎的历史,《JS引擎(0):起底各种JavaScript引擎群雄争霸之路

一些流行的 JavaScript 引擎

  • SpiderMonkey ,Brendan Eich 在Netscape创建,由 C/C++ 语言开发,可适配 ECMA-262 Edition 5 及其之后的标准版本

  • Rhino,由 Norris Boyd(归属Netscape)创建,则是一个 Java 语言开发的 JavaScript 实现,跟SpiderMonkey 

  • Google 的 V8,在 Google Chrome 浏览器和较新的 Opera 浏览器中使用。这同时也是Node.js使用的引擎。

  • JavaScriptCore (SquirrelFish/Nitro),被用在了一些 WebKit 浏览器如 Apple Safari。

  • Carakan,用在旧版本 Opera 中。

  • The Chakra 引擎

几种较老的JavaScript引擎的特征:

不懂的术语,可以跳转至 JavaScript引擎相关关术语解析

  SpiderMonkey JScript KJS
实现语言 C C++ C++
执行模式 解释执行 解释执行 解释执行
解释器 字节码解释器:基于栈的字节码 字节码解释器:基于栈的字节码 树遍历解释器
动态编译器
自动内存管理 mark-and-sweep mark-and-sweep mark-and-sweep
对象布局 ? 基本上是HashTable ?
针对密集数组的优化 ? 无 (JScript < 5.7);有(JScript 5.8) ?
Inline-cache ? ? ?
值表现形式 tagged-value 堆对象 堆对象
Function.prototype.toString() 从字节码反编译 ? ?

在Google推出V8之后,业界受到巨大冲击。V8的性能远高于当时所有其它JavaScript引擎,可以有效支撑起当时兴起的大量使用JavaScript的Web应用。

各大JavaScript引擎的实现者都坐不住了,像打了鸡血似的使劲优化优化再优化。先是把已在其它HLLVM上得到充分验证的优化技术引入到JavaScript引擎中,然后再针对JavaScript语言的特点做专项优化。

现在(2013-04)几种主流的JavaScript引擎的特征:

  V8 SpiderMonkey Chakra Nitro Nashorn
实现语言 C++/汇编 C++ C++ C++/汇编 Java
执行模式 纯编译: 两层编译 解释/编译混合式: 3层执行模式 解释/编译混合: 2层执行模式,后台编译 解释/编译混合: 3层执行模式 纯编译
解释器 字节码解释器 字节码解释器:基于寄存器的字节码 字节码解释器 LLInt:基于寄存器的字节码
动态编译器 初级编译器 + 优化编译器 初级编译器 Baseline + 优化编译器 IonMonkey 初级编译器 method JIT + 优化编译器 DFG JIT
自动内存管理

分代式GC: 初生代: copying收集器;

 年老代: 增量式mark-and-sweep, 可选compact

分代式GC

分代式GC: 初生代: copying收集;

 年老代: 并发式mark-and-sweep

分代式GC 依赖于底层JVM的GC
对象布局 紧凑+隐藏类 Map 紧凑+隐藏类 Shape 紧凑+隐藏类 紧凑+隐藏类 Structure 紧凑+隐藏类 PropertyMap
针对密集数组的优化
Inline-cache MIC/PIC PIC PIC PIC MIC/PIC
值表现形式 tagged-pointer / IEEE 754 double / integer pun-boxing tagged-value NaN-boxing   堆对象 / integer
正则表达式 编译 Irregexp 编译 编译 编译 WREC 混合
Function. prototype. toString() 保留源码原文 (2012年7月前) 从字节码反编译; (761723后) 保留源码原文  ? ? 保留源码原文

 

JavaScript引擎相关术语解析

  • 树遍历解释器:tree-walking interpreter。遍历抽象语法树来解释执行的解释器。

  • 对象布局: object representation 或者 object layout。指在堆上分配的JavaScript对象的在内存中的布局。

  • 值表现形式: value representation。注意跟“对象布局”说的不是一件事。这个指的是原始类型数据、指向堆上分配的对象的指针之类的值的表现形式。对某些JavaScript引擎来说这是指“JSValue”背后在内存中的表现形式。新生代中的对象98%是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性的复制到另外一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性的复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存为整个新生代容量的90%(80%+10%),只有10%的内存会被“浪费”。

  • copying GC: 也叫scavenger。垃圾收集算法——复制算法,他将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。

    现在的商业虚拟机都采用这种收集算法来回收新生代,这种算法的代价是将内存缩小为了原来的一半

    • IC,Inline caching,内联缓存。实际上是一门近30年的非常古老的技术,最初用在Smalltalk虚拟机上。工作原理:创建一个高速路来绕过运行时系统来读取对象的属性:对传入的对象及其属性作出某种假设,然后通过一个低成本的方式验证这个假设是否正确,如果正确就读取上次缓存的结果。在充满了动态类型和晚绑定以及其他古怪行为——比如eval——的语言里对一个对象作出合理的假设是非常困难的,所以我们退而求其次,让我们的读/写操作能够有学习能力:一旦它们看见某个对象它们就可以以某种方式来自适应,使得之后的读取操作在遇到类似结构的对象时能够更快地进行。在某种意义上,我们将要在读/写操作上缓存关于之前见过的对象的布局的相关知识——这也是内联缓存这个名字的由来。内联缓存可以被用在几乎所有需要动态行为的操作上,只要你可以找到正确的高速路:算数操作、调用自由函数、方法调用等等。有些内联缓存还能缓存不止一条快速通道,这些内联缓存就变成了多态的。

    • MIC: monomorphic inline-cache,单态内联缓存。有一个简单的直接类型检查开销,然后是普通的直接调用开销。

  • PIC: polymorphic inline-cache,多态内联缓存

  • pun-boxing: Packed NaN unboxing,SpiderMonkey和LuaJIT似乎都在用pun boxing

当代JavaScript引擎之间有许多共通的实现技巧

当代JavaScript引擎之间有许多共通的实现技巧。多数优化会对JavaScript程序的行为做一定猜测(speculate),并基于猜测做激进优化(speculative optimization)。下面挑几个简单介绍一下。

从源语言到中间表示的编译器(source-to-IR compiler)

  也叫做编译器的“前端”。

  递归下降式语法分析器(recursive-descent parser)

  运算符优先级式语法分析器(operator precedence parser)

  deferred parser / diet parser(延迟语法分析)

从中间表示到目标代码的编译器(IR-to-target-code compiler)

  也叫做编译器的“后端”。但因为这部分编译器经常被叫做“JIT”编译器,所以单独拿出来写

  JIT style compiler: “just-in-time编译”狭义的定义是“即时编译”,也就是在某段代码即将第一次被执行时才对其编译。太早或太迟都不符合这个狭义版定义。所谓“JIT风格的编译器”通常意味着“编译是同步进行的”。这就自然的引出几个特征:

  1. 编译速度必须很快;

  2. 编译只能做有限的优化,只能选效费比高的来做。

optimizing compiler

  • 多层编译(tiered compilation)

  • 后台编译(background compilation)

  • 类型反馈(type feedback)

  • 类型特化(type specialization)

  • SSA-form IR

自动内存管理

  • 分代式GC(generational GC)

  • 增量式GC(incremental GC)

  • 并发式GC(concurrent GC)

  • 准确式GC(exact / accurate / type exact / type accurate / precise GC)

对象布局

  • 紧凑对象布局 + 隐藏类

值表现形式

  • tagger-pointer 或 tagged-value

  • NaN-boxing

运行时系统

  • inline-cache

  • on-stack replacement

  • deoptimization

  • 用native stack实现VM stack

  • cons-string 或者叫 rope 来优化字符串拼接

  • dependent string/sliced string 来优化字符串的子串操作

  • sparse array

  • B-tree

上面介绍的JavaScript引擎实现技巧也影响了“如何写出更高效的JavaScript代码”:尽量让代码的行为符合JavaScript引擎的猜测,效率就会高。

写类型稳定的代码

  • 在构造器函数里声明和初始化所有属性

  • 尽量不要delete属性;不要通过delete属性来把某个属性重置,赋值为undefined都好

  • 不要把数组当一般对象用;不要把一般对象当数组用

参考内容:

各JavaScript引擎的简介,及相关资料/博客收集帖 https://hllvm-group.iteye.com/group/topic/37596

 

 


转载本站文章《JS引擎(1):JS引擎擂台赛,JavaScript引擎的特征比较及术语科普》,
请注明出处:https://www.zhoulujun.cn/html/webfront/browser/webkit/2020_0718_8522.html

标签:对象,JavaScript,JS,编译,编译器,引擎,GC,擂台赛
From: https://www.cnblogs.com/zhoulujun/p/17300722.html

相关文章

  • JS引擎(2):Java平台上JavaScript引擎—Rhino/Nashorn概述
    可以后端开发的javascript引擎有ChromeV8基于C++java的Rhino引擎(JDK6被植入),Java8被替换为NashornRhino和Nashorn都是用Java实现的JavaScript引擎。它们自身都是普通的Java程序,运行在JVM上Rhino简介Rhino[ˈraɪnəʊ]是一种使用Java语言编写的JavaScript的......
  • JS引擎(0):JavaScript引擎群雄演义—起底JavaScript引擎
    JavaScript既是一个 面向过程的语言 又是一个 面向对象的语言。在JavaScript中,通过在运行时给空对象附加方法和属性来创建对象,与编译语言如C++和Java中常见的通过语法来定义类相反。对象构造后,它可以用作是创建相似对象的原型。JavaScript的动态特性包括运行时构造对......
  • 浏览器层面优化前端性能(2):Reader引擎线程与模块分析优化点
    Reader引擎线程与模块分析 首先是网页内容,加载完输入到HTML解释器,解释后构成DOM树,这期间如果遇到JavaScript代码就交给JavaScript引擎去处理,如果网页中包含CSS,就交给CSS解释器;DOM树简历的时候,渲染引擎接收来自CSS解释器的样式信息,构建一个新的你日不会吐模型,该模型由布局模......
  • 使用eval的fromCharCode方法对js代码加壳
    在JavaScript中,使用eval函数可以将字符串作为代码来执行。这个特性可以被用来对JavaScript代码进行加壳以增加代码的安全性和保护知识产权。其中一个常用的方法是通过String.fromCharCode方法来创建一系列的ASCII字符,并将其拼接成一个包含加密代码的字符串。然后再通过eval函数执行......
  • Content type 'application/json' not supported] (已解决)
    今天在用postman发请求时,以Json的形式发送一个POJO参数,结果一直报错:WARNING]Resolved[org.springframework.web.HttpMediaTypeNotSupportedException:Contenttype'application/json'notsupported],如下图所示:经过检查各方面都没有问题,最发现是在SpringMVC配置类中没有加 ......
  • JavaWeb-JSP-JSTL c foreach -2023-04-09
    <%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%><%@pageimport="java.util.ArrayList"%><%@pagecontentType="text/html;charset=UTF-8"language="java"%><html>&l......
  • JavaWeb-JSP JSTL标签 -2023-04-09
    <%--CreatedbyIntelliJIDEA.User:AdministratorDate:2023/4/9Time:15:10TochangethistemplateuseFile|Settings|FileTemplates.--%><%@pagecontentType="text/html;charset=UTF-8"language="java"%>&l......
  • js异步——事件循环和消息队列
    前言上篇文章中介绍了多进程的浏览器基本架构,现在,我们来谈谈单线程的JS代码、消息队列、事件循环、微任务和宏任务。单线程的JavaScript什么是单线程js?如果你已经仔细阅读过上一篇文章,那么答案是显而易见的:由于浏览器是由渲染进程的主线程来执行js代码的,换句话说,js的运......
  • uniapp如何下载video.js
    在uni-app中引入video.js有两种方式1.通过cdn的方式引入(不建议,当这个cdn失效时,你的业务可能就崩了)<linkhref="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.3.0/video-js.min.css"rel="stylesheet"><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/vide......
  • js异步——浅谈Chrome浏览器架构
    前言在讲述事件循环和消息队列之前,需要了解JS的单线程执行机制,JS的执行是从上到下依次执行的,这些便是同步任务,而异步操作类似于系统中断,即当前进程外部的实体(主线程之外的、宿主环境提供的、特殊的线程,如IO线程(HTTP请求)和定时器线程等)可以触发代码执行,然后在异步任务完毕后,执......