概述

一个 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 引擎中书写最优代码的更多相关文章

  1. How Javascript works (Javascript工作原理) (二) 引擎,运行时,如何在 V8 引擎中书写最优代码的 5 条小技巧

    个人总结: 一个Javascript引擎由一个标准解释程序,或者即时编译器来实现. 解释器(Interpreter): 解释一行,执行一行. 编译器(Compiler): 全部编译成机器码,统一执行. ...

  2. Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

    V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 一.前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存.垃圾 ...

  3. 浅谈Chrome V8引擎中的垃圾回收机制

    垃圾回收器 JavaScript的垃圾回收器 JavaScript使用垃圾回收机制来自动管理内存.垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担,减少因 长时间运转而带 ...

  4. 浅谈V8引擎中的垃圾回收机制

    最近在看<深入浅出nodejs>关于V8垃圾回收机制的章节,转自:http://blog.segmentfault.com/skyinlayer/1190000000440270 这篇文章 ...

  5. 使用 D8 分析 javascript 如何被 V8 引擎优化的

    在上一篇文章中我们讲了如何使用 GN 编译 V8 源码,文章最后编译完成的可执行文件并不是 V8,而是 D8.这篇我们讲一下如何使用 D8 调试 javascript 代码. 如果没有 d8,可以使用 ...

  6. How Javascript works (Javascript工作原理) (一) 引擎,运行时,函数调用栈

    个人总结:该系列文章对JS底层的工作原理进行了介绍. 这篇文章讲了 运行时:js其实是和AJAX.DOM.Settimeout等WebAPI独立分离开的 调用栈:JavaScript的堆内存管理 和 ...

  7. JavaScript深入浅出第4课:V8引擎是如何工作的?

    摘要: 性能彪悍的V8引擎. <JavaScript深入浅出>系列: JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼? JavaScript深入浅出第2课:函数是一等 ...

  8. v8引擎详解(摘)-- V8引擎是一个JavaScript引擎实现

    随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScript脚本.V8引擎就是为解决这一问题而生,在node中也 ...

  9. V8引擎嵌入指南

    如果已读过V8编程入门那你已经熟悉了如句柄(handle).作用域(scope)和上下文(context)之类的关键概念,以及如何将V8引擎作为一个独立的虚拟机来使用.本文将进一步讨论这些概念,并介绍 ...

随机推荐

  1. javabean 数组对应yml中的写法

    gate-info: gate-list: - channel: channel-one io-flag: I - channel: channel-two io-flag: E 上面的是 yml 文 ...

  2. Phoneix(四)hbase导入数据同时与phoenix实现映射同步

    一.说明 先创建一个hbase表格,能够导入本地数据到hbase中,最后能够通过phoneix进行访问. 1.数据准备(10W条,样例如下),文件test.txt 0,20190520164020,1 ...

  3. mysql高并发解决方案

    mysql高并发的解决方法有: 优化SQL语句,优化数据库字段,加缓存,分区表,读写分离以及垂直拆分,解耦模块,水平切分等. 高并发大多的瓶颈在后台,在存储mysql的正常的优化方案如下: (1)代码 ...

  4. 【详细】Python基础(一)

    @ 目录 前言 1. Python环境的搭建 1.1 python解释器的安装 1.2 pycharm的安装 2. Python基础语法 2.1 基本语法 2.2 数据类型 2.3 标识符与关键字 2 ...

  5. Laya 踩坑日记-人物模型穿模,模型显示不正常

    最近做游戏,人物要跑到很远的位置,z轴距离大概有20000个单位,然后就发现一个bug,到远处人物模型穿了,而且没办法改,这就尴尬了 Z轴对应值    0    100000 100000 当距离零点 ...

  6. update 表名 set 某列名=now() where user in('user1','user2','user3');

    update 表名  set 某列名=now() where user in('user1','user2','user3');

  7. cobalt strike出现连接超时情况解决办法

    服务器安装好teamserver服务后,进行连接,此时出现了连接超时的情况 检查方法: 一.检查端口是否正常开启 netstat -an | grep <设置的端口号>centos7可以用 ...

  8. [Usaco2016 Dec]Moocast

    原题链接https://www.lydsy.com/JudgeOnline/problem.php?id=4749 可以对于每个点\(i\),往跟\(i\)距离小于等于\(p[i]\)的点\(j\)都 ...

  9. pytest学习笔记(pytest框架结构)

    一.pytest框架中使用setup.teardown.更灵活按照用例级别可以分为以下几类: 1.模块级:(setup_module.teardown_module)在模块始末调用 2.函数级:(se ...

  10. Java 8中字符串拼接新姿势:StringJoiner

    介绍 StringJoiner是java.util包中的一个类,用于构造一个由分隔符分隔的字符序列(可选),并且可以从提供的前缀开始并以提供的后缀结尾.虽然这也可以在StringBuilder类的帮助 ...