概述

一个 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. 第8章 控制对象的访问(setter、getter、proxy)

    目录 1. 使用getter和setter控制属性访问 1.1 定义getter与setter 通过对象字面量定义,或在ES6的class中定义 通过使用内置的Object.definePropert ...

  2. 利用dotnet-dump分析docker容器内存泄露

    目录 一 运行官方示例 1,Clone代码并编译 2,创建Dockerfile构建镜像 3,启动容器 二 生成dump转储文件 1,制造问题 2,创建dump文件 三 分析dump文件 1,创建一个用 ...

  3. 算法历练之路——入学考试(JAVA)

    入学考试 时间限制: 1Sec 内存限制: 128MB 提交: 42 解决: 18 题目描述辰辰是个天资聪颖的孩子,他的梦想是成为世界 上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断 ...

  4. linux环境下oracle 11g 静默安装

    安装环境 Linux服务器:oracle linux 6.6 64位 Oracle服务器:Oracle11gR2 64位 系统要求 1.Linux安装Oracle系统要求 系统要求 说明 内存 必须高 ...

  5. 解锁Renderbus客户端使用新技巧----快速渲染效果图篇

    度娘说,效果图最基本的要求就是:应该符合事物的本身尺寸,不能为了美观而使用效果把相关模型的尺寸变动,那样的效果图不但不能起到表现设计的作用,反而成为影响设计的一个因素.可见高效渲染效果图是都是当下我们 ...

  6. SpringBoot配置文件(1)

    配置文件 1.配置文件 SpringBoot使用一个全局的配置文件 application.properties application.yml 配置文件名是固定的: 他的作用是修改SpringBoo ...

  7. ctfshow—web—web3

    打开靶机 提示是文件包含漏洞 测试成功 https://d7c9f3d7-64d2-4110-a14b-74c61f65893c.chall.ctf.show/?url=../../../../../ ...

  8. 【工具篇】Mysql的安装和使用

    [导读]Mysql是数据分析师入门级的技能之一,对于很多小白同学来说,可能还没有机会接触SQL知识.那么我们如何熟悉和练习SQL呢,今天教大家安装两个软件:MySQL和Navicat.后续我们会推出S ...

  9. 二. SpringCloud基本Rest微服务工程搭建

    1. 父工程构建 1.1 Maven项目搭建 环境 版本 JDK 1.8 Maven 3.6+ Maven模板 maven-archetype-size 删除父工程src文件 1.2 父工程pom文件 ...

  10. kettle数据质量统计

    1.利用Kettle的"分组","JavaScript代码","字段选择"组件,实现数据质量统计.2.熟练掌握"JavaScrip ...