JavaScript(二)——在 V8 引擎中书写最优代码
概述
一个 JavaScript 引擎就是一个程序或者一个解释程序,它运行 JavaScript 代码。一个 JavaScript 引擎可以用标准解释程序或者即时编译器来实现,即时编译器即以某种形式把 JavaScript 解释为字节码。
V8 引擎的由来
V8 引擎是由谷歌开源并以 C++ 语言编写。Google Chrome 内置了这个引擎。而 V8 引擎不同于其它引擎的地方在于,它也被应用于时下流行的 Node.js 运行时中。
起先 V8 是被设计用来优化网页浏览器中的 JavaScript 的运行性能。为了达到更快的执行速度,V8 把 JavaScript 代码转化为更加高效的机器码而不是使用解释程序。它通过实现一个即时编译器在运行阶段把 JavaScript 代码编译为机器码,就像诸如 SpiderMonkey or Rhino (Mozilla) 等许多现代 JavaScript 引擎所做的那样。
主要的区别在于 V8 不产生字节码或者任何的中间码。
内联
第一个优化方法即是提前尽可能多地内联代码。内联指的是把调用地址(函数被调用的那行代码)置换为被调用函数的函数体的过程。这个简单的步骤使得接下来的代码优化更有意义。
隐藏类
JavaScript 是基于原型的语言:当进行克隆的时候不会有创建类和对象。JavaScript 也是一门动态编程语言,这意味着在它实例化之后,可以任意地添加或者移除属性。
大多数的 JavaScript 解释器使用类字典的结构(基于哈希函数)在内存中存储对象属性值的内存地址(即对象的内存地址)。
由于使用字典在内存中寻找对象属性的内存地址是非常低效的,V8 转而使用隐藏类。隐藏类工作原理和诸如 Java 语言中使用的固定对象布局(类)相似,除了它们是在运行时创建的以外。
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
一旦 “new Point(1,2)” 调用发生,V8 他创建一个叫做 “C0” 的隐藏类。
因为还没有为类 Point 创建属性,所以 “C0” 是空的。
一旦第一条语句 “this.x = x” 开始执行(在 Point 函数中), V8 将会基于 “C0” 创建第二个隐藏类。”C1” 描述了可以找到 x 属性的内存地址(相对于对象指针)。本例中,”x” 存储在位移 0 中,这意味着当以内存中连续的缓冲区来查看点对象的时候,位移起始处即和属性 “x” 保持一致。V8 将会使用 “类转换” 来更新 “C0”,”类转换” 即表示属性 “x” 是否被添加进点对象,隐藏类将会从 “C0” 转为 “C1”。以下的点对象的隐藏类现在是 “C1”。
每当对象添加新的属性,使用转换路径来把旧的隐藏类更新为新的隐藏类。隐藏类转换是重要的,因为它们使得以同样方式创建的对象可以共享隐藏类。如果两个对象共享一个隐藏类并且两个对象添加了相同的属性,转换会保证两个对象收到相同的新的隐藏类并且所有的优化过的代码都会包含这些新的隐藏类。
一个被称为 “C2” 的隐藏类被创造出来,一个类转换被添加进 “C1” 中表示属性 “y” 是否被添加进点对象(已经拥有属性 “x”)之后隐藏会更改为 “C2”,然后点对象的隐藏类会更新为 “C2”。
隐藏类转换依赖于属性被添加进对象的顺序。看如下的代码片段:
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
p1.a = 5;
p1.b = 6;
var p2 = new Point(3, 4);
p2.b = 7;
p2.a = 8;
对于 “p1”,先添加属性 “a” 然后再添加属性 “b”。对于 “p2”,先添加属性 “b” 然后是 “a”。这样,因为使用不同的转换路径,”p1” 和 “p2” 会使用不同的隐藏类。在这种情况下,更好的方法是以相同的顺序初始化动态属性以便于复用隐藏类。
内联缓存
内联缓存依赖于对于同样类型的对象的同样方法的重复调用的观察。
编译为机器码
垃圾回收
V8 使用传统的标记-清除技术来清理老旧的内存以进行垃圾回收。标记阶段会中止 JavaScript 的运行。为了控制垃圾回收的成本并且使得代码执行更加稳定,V8 使用增量标记法:不遍历整个内存堆,试图标记每个可能的对象,它只是遍历一部分堆,然后重启正常的代码执行。下一个垃圾回收点将会从上一个堆遍历中止的地方开始执行。这会在正常的代码执行过程中有一个非常短暂的间隙。
Ignition 和 TurboFan
如何写优化的 JavaScript 代码
- 对象属性的顺序:总是以相同的顺序实例化你的对象属性,这样你的隐藏类及之后的优化代码都可以被共享。
- 动态属性:实例化之后为对象添加属性会致使为之前隐藏类优化的方法变慢。相反,在对象构造函数中赋值对象的所有属性。
- 方法:重复执行相同方法的代码会比每次运行不同的方法的代码更快(多亏了内联缓存)。
- 数列:避免使用键不是递增数字的稀疏数列。稀疏数列中没有包含每个元素的数列称为一个哈希表。访问该数列中的元素会更加耗时。同样地,试着避免预先分配大型数组。最好是随着你使用而递增。最后,不要删除数列中的元素。这会让键稀疏。
- 标记值:V8 用 32 位来表示对象和数字。它使用一位来辨别是对象(flag=1)或者是被称为 SMI(小整数) 的整数(flag=0),之所以是小整数是因为它是 31 位的。之后,如果一个数值比 31 位还要大,V8 将会装箱数字,把它转化为浮点数并且创建一个新的对象来存储这个数字。尽可能试着使用 31 位有符号数字来避免创建 JS 对象的耗时装箱操作。
作者:tristan
链接:https://juejin.im/post/5ae1c2936fb9a07a9c03ec1c
JavaScript(二)——在 V8 引擎中书写最优代码的更多相关文章
- How Javascript works (Javascript工作原理) (二) 引擎,运行时,如何在 V8 引擎中书写最优代码的 5 条小技巧
个人总结: 一个Javascript引擎由一个标准解释程序,或者即时编译器来实现. 解释器(Interpreter): 解释一行,执行一行. 编译器(Compiler): 全部编译成机器码,统一执行. ...
- Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略
V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 一.前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存.垃圾 ...
- 浅谈Chrome V8引擎中的垃圾回收机制
垃圾回收器 JavaScript的垃圾回收器 JavaScript使用垃圾回收机制来自动管理内存.垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担,减少因 长时间运转而带 ...
- 浅谈V8引擎中的垃圾回收机制
最近在看<深入浅出nodejs>关于V8垃圾回收机制的章节,转自:http://blog.segmentfault.com/skyinlayer/1190000000440270 这篇文章 ...
- 使用 D8 分析 javascript 如何被 V8 引擎优化的
在上一篇文章中我们讲了如何使用 GN 编译 V8 源码,文章最后编译完成的可执行文件并不是 V8,而是 D8.这篇我们讲一下如何使用 D8 调试 javascript 代码. 如果没有 d8,可以使用 ...
- How Javascript works (Javascript工作原理) (一) 引擎,运行时,函数调用栈
个人总结:该系列文章对JS底层的工作原理进行了介绍. 这篇文章讲了 运行时:js其实是和AJAX.DOM.Settimeout等WebAPI独立分离开的 调用栈:JavaScript的堆内存管理 和 ...
- JavaScript深入浅出第4课:V8引擎是如何工作的?
摘要: 性能彪悍的V8引擎. <JavaScript深入浅出>系列: JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼? JavaScript深入浅出第2课:函数是一等 ...
- v8引擎详解(摘)-- V8引擎是一个JavaScript引擎实现
随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScript脚本.V8引擎就是为解决这一问题而生,在node中也 ...
- V8引擎嵌入指南
如果已读过V8编程入门那你已经熟悉了如句柄(handle).作用域(scope)和上下文(context)之类的关键概念,以及如何将V8引擎作为一个独立的虚拟机来使用.本文将进一步讨论这些概念,并介绍 ...
随机推荐
- eclipse开启的时候adb.exe会自动开启么 怎么让它跟着eclipse自动开启
会的,Eclipse 参数页中的 General > Startup and Shutdown 中默认 Android Development Tools 是自动激活的,它会启动 adb.exe ...
- uniapp H5引入腾讯地图
在网上搜索了许多关于uniapp引入腾讯地图的方法都以失败告终,我开发的应用主要使用于H5,小程序与H5是不同的sdk,就不在这说了,况且小程序有手把手教学,可参考腾讯地图官网https://lbs. ...
- 认识PHP8
PHP 团队于2020年11月26日宣布 PHP 8 正式发布!这意味着将不会有 PHP 7.5 版本.PHP8 目前正处于非常活跃的开发阶段,所以在接下来的几个月里,情况可能会发生很大的变化.我也分 ...
- IDEA maven项目报错,找不到或无法找到主类
最近在写UDF,依赖复制的公司的依赖, <dependency> <groupId>org.apache.hive</groupId> <artifactId ...
- 浅入 .NET Core 中的内存和GC知识
目录 托管代码 自动内存管理 参考资料: [1]https://docs.microsoft.com/zh-cn/dotnet/standard/managed-code [2]:https://do ...
- 常用 .gitignore 模板
前言 每次建项目的时候可以直接复制了,也算是方便自己,以后发现少的会更新 正文 作用 git提交时忽略文件 文件名 .gitignore Python # Byte-compiled / optimi ...
- git的使用命令总结
git一般方法git init 本地目录生成仓库git clone +项目url地址 克隆远程仓库git status 查看状态git pull 拉取 拉取的代码在用户家目录下git push 上传g ...
- k8s之ServiceAccount
导读 上一篇说了k8s的RBAC授权模式,今天就来简单看一下其中涉及到的ServiceAccount. 简介 k8s创建两套独立的账号系统,原因如下: (1)User账号给用户用,Service Ac ...
- Lnux:实验 Linux C 编程
实验题目: 实验 3 Linux C 编程 实验目的和要求: 熟悉 Linux 操作系统环境 在 Linux 下编写.执行简单的 C 程序 用 C 语言写自己的 Linux 命令 实验过程: 认真 ...
- druid discard long time none received connection问题解析
最新项目中用的druid连接数据库遇到一个困扰很久的问题 1 开始用的druid版本是1.1.22版本,由于业务需求,单个连接需要执行很久,理论上不需要用到自动回收,但为了安全,还是加了自动回收,时间 ...