[翻译] V8引擎的解析
本文档介绍了 V8 引擎是如何解析 JavaScript 源代码的,以及我们将改进它的计划。
动机
我们有个解析器和一个更快的预解析器(~2x),但是预解析器对大多数现代 JavaScript 无用。此外假如还没有编译了外部函数,否则我们必须再解析一遍内部函数。 那现在的 V8 引擎什么时候会立即编译(eager compilation)呢?
顶层的括号函数(function...
在 ( 之后的不是函数声明的内部函数
当我们把一个内部函数当做非脚本级的一部分来解析的时候,是不能使用预解析器的。我们将不会编译一个内部函数,所以如果我们永远不先行编译函数,那么一般的 n 层嵌套函数会先被预解析,然后会有 n 次解析(以及一次编译)。或从另一方面来看待它,考虑了以下格式的顶级“模块”:
!function f() {
function A() {
function B() {...}
}
function C() {
function D() {...}
}
...
}(), function g() {....}()
压缩程序经常用 (function() {...})();(function(){...}))() 替换上面的代码。但是,你可以看到它没有被括号括起来,所以我们不会在顶层运行完整的解析器。这是为什么呢?因为我们不知道这些函数会被立即调用。因此会使用更快的预解析器。但是确实需要这些函数,这就打脸了,而 V8 就不得不去再解析。预解析阶段会将整个顶层函数看一遍,包括 A, B, C, D(假如原本的 (function f() {...})() 编译过那么就会跳过本阶段)。
因为顶层函数被调用了,所以引擎会去解析它。这次解析是完全的。为什么呢?因为需要做范围解析以便知道在哪里分配变量。而唯一的正确的方式是知道什么变量被引用了。而知道什么变量被引用的唯一方式是对内部函数也进行完全的解析。所以解析/编译顶层函数会迫使引擎对 A,B,C,D 这些函数也进行完全解析。
现在我们需要调用函数 A,因此需要去编译它。为了解析它,我们也需要知道在哪里分配变量。就像我说的:你需要知道从内部函数引用了上面。所以我们完全解析了 B 函数。
现在我们假定预解析需要1费,解析需要2费。编译它需要另外的2费,但是我们实际上编译的是压缩的版本,因此可以忽略。 假如现在运行 A 函数,那么将花费 3*(f + A + B) + 2*(A + B)。如果 A 将会调用 B,我们就花费另外的2费用于 B 函数。
一方面,要得到一个内部函数,你需要解析一大堆。 另一方面,顶层函数越多,解析它的成本就越高,因为你要算上解析所有嵌套函数的时间。
建议的解决方案
那么计划怎么解决呢?
预解析的同时也进行范围解析(scope resolution),这样未编译部分的花费会从2降低到1.x。
将函数的上下文内存分配信息序列化到持久存储中,以避免不必要的重解析成本。
立即编译可能会支持 ! 和 , 。
至于成本?所有懒解析函数在初始加载时的开销为1.x,如果实际使用则为3.x。.x是内部函数范围解析和序列化的额外未知成本。
立即编译的优点是,对于已知的立即执行的顶级函数,我们可以进一步将成本从3.x下降到2。它是从顶层向内的。如果我们决定不将预解析(eagerly parse)作为主编译工作的一部分,那么我们可以等到它被执行,这样我们至少可以确定只需要支付实际使用的功能的编译成本(2解析和2编译)。
立即编译的缺点是我们需要在解析和编译之间在内存中保持AST(Abstract syntax tree)。显著增加了使用内存的峰值。如果我们可以预解析那些在被立即解析的函数的内部函数,情况可能会看起来好多了。即使如此,在低内存设备上,我们应该禁用启发式的立即编译。
如果我们序列化这些数据,那么在热启动时就完完全全不需要去查看未使用的代码。热启动时,即使在顶层的时候也是与启发式的立即编译不相关,因为我们将只解析/编译我们需要的函数。 搭载了比使用的代码还要多10倍以上的页面将会立即热启动。(目前已经可以是这种情况,但它是一个有点hit-and-miss)。
相关链接:
[翻译] V8引擎的解析的更多相关文章
- 深入V8引擎-引擎内部类管理解析
v8的初始化三部曲,前面花了三篇解决了第一步,由于只是生成了一个对象,第二步就是将其嵌入v8中,先看一下三个步骤. // 生成默认Platform对象 std::unique_ptr<v8::P ...
- jQuery 2.0.3 源码分析Sizzle引擎 - 词法解析
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排.各家浏览器引擎的工作原理略有差别,但也有一定规则. 简 ...
- 分析Sizzle引擎 - 词法解析
分析Sizzle引擎 - 词法解析 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排.各家浏览器引擎的工 ...
- V8引擎嵌入指南
如果已读过V8编程入门那你已经熟悉了如句柄(handle).作用域(scope)和上下文(context)之类的关键概念,以及如何将V8引擎作为一个独立的虚拟机来使用.本文将进一步讨论这些概念,并介绍 ...
- JavaScript工作机制:V8 引擎内部机制及如何编写优化代码的5个诀窍
概述 JavaScript引擎是一个执行JavaScript代码的程序或解释器.JavaScript引擎可以被实现为标准解释器,或者实现为以某种形式将JavaScript编译为字节码的即时编译器. 下 ...
- JavaScript是如何工作的02:深入V8引擎&编写优化代码的5个技巧
概述 JavaScript引擎是执行 JavaScript 代码的程序或解释器.JavaScript引擎可以实现为标准解释器,或者以某种形式将JavaScript编译为字节码的即时编译器. 以为实现J ...
- 深入浏览器工作原理和JS引擎(V8引擎为例)
浏览器工作原理和JS引擎 1.浏览器工作原理 在浏览器中输入查找内容,浏览器是怎样将页面加载出来的?以及JavaScript代码在浏览器中是如何被执行的? 大概流程可观察以下图: 首先,用户在浏览器搜 ...
- 高性能JavaScript模板引擎原理解析
随着 web 发展,前端应用变得越来越复杂,基于后端的 javascript(Node.js) 也开始崭露头角,此时 javascript 被寄予了更大的期望,与此同时 javascript MVC ...
- 精读《V8 引擎 Lazy Parsing》
1. 引言 本周精读的文章是 V8 引擎 Lazy Parsing,看看 V8 引擎为了优化性能,做了怎样的尝试吧! 这篇文章介绍的优化技术叫 preparser,是通过跳过不必要函数编译的方式优化性 ...
随机推荐
- 高访问量WEB开发中的架构模式,学习从点滴开始
当一个Web系统从日访问量10万逐步增长到1000万,甚至超过1亿的过程中,Web系统承受的压力会越来越大,在这个过程中,我们会遇到很多的问题.为了解决这些性能压力带来问题,我们需要在Web系统架构 ...
- springmvc+spring+mybatis+maven项目集成shiro进行用户权限控制【转】
项目结构: 1.maven项目的pom中引入shiro所需的jar包依赖关系 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...
- javascript操作系统检测
function detectOS() { var sUserAgent = navigator.userAgent;console.log(sUserAgent); var isWin = (nav ...
- 利用github pages创建简单的网站
github.com 作为最流行的源代码管理工具已经风靡全球,同时在依托于github也衍生出了各种各样的应用,比如可以利用github搭建博客系统等等. 先换个话题,我们每人手头都或多或少有些&qu ...
- Jsp的九个内置对象
一.什么是内置对象?在jsp开发中,会频繁使用到一些对象.例如HttSession,ServletContext,HttpServletRequest.如果我们每次要使用这些对象都去创建这些对象,就会 ...
- ORA-00600 3020 ORA-10567案例
PlateSpin克隆复制出的Oracle数据库服务器,往往启动数据库实例都会遇到一些杂七杂八的问题.今天测试DR环境时又遇到了一个特殊场景,在此之前,我已经遇到了下面两起案例: ORA-00600: ...
- SQL Server 2012 新特性:FileTable
FileTable是基于FILESTREAM的一个特性.有以下一些功能: 一行表示一个文件或者目录. 每行包含以下信息: file_Stream流数据,stream_id标示符(GUID). 用户表示 ...
- mariadb数据库忘记密码如何找回
1.systemctl stop mariadb ==>停止mariadb数据库 2.mysqld_safe --skip-grant-tables & ==>进入单机模式 3.m ...
- 分布式一致性算法--Raft
前面一篇文章讲了Paxos协议,这篇文章讲它的姊妹篇Raft协议,相对于Paxos协议,Raft协议更为简单,也更容易工程实现.有关Raft协议和工程实现可以参考这个链接https://raft.gi ...
- RHEL6和RHEL7恢复root用户密码
一.RHEL6恢复root密码 将系统重启,出现如下界面按上下键选择会停住,并输入e键 选中下图红框选项,再输入e键 再输入1,进入单用户模式 输入b进行启动 修改密码,然后重启 二.RHEL7恢 ...