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引擎作为一个独立的虚拟机来使用.本文将进一步讨论这些概念,并介绍 ...
随机推荐
- JAVA并发包——锁
1.java多线程中,可以使用synchronized关键字来实现线程间的同步互斥工作,其实还有个更优秀的机制来完成这个同步互斥的工作--Lock对象,主要有2种锁:重入锁和读写锁,它们比synchr ...
- noip2016自测报告
随便测了一下2016的提高组,发现自己还是太菜了... day1 T1:玩具谜题 一个模拟题,打挂了,只有95分,错的很傻逼 code: #include<bits/stdc++.h> u ...
- elasticsearch基本概念和基本语法
Elasticsearch是基于Json的分布式搜索和分析引擎,是利用倒排索引实现的全文索引. 优势: 横向可扩展性:增加服务器可直接配置在集群中 分片机制提供更好的分布性:分而治之的方式来提升处理效 ...
- 双重校验锁 --使用volatile和两次判空校验
介绍 双重校验锁是单例模式中,饿汉式的一种实现方式.因为有两次判空校验,所以叫双重校验锁,一次是在同步代码块外,一次是在同步代码块内. 为什么在同步代码块内还要再检验一次? 第一个if减少性能开销,第 ...
- java对象
原文链接http://zhhll.icu/2020/04/26/java%E5%9F%BA%E7%A1%80/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/java%E5% ...
- SpringBoot配置文件(1)
配置文件 1.配置文件 SpringBoot使用一个全局的配置文件 application.properties application.yml 配置文件名是固定的: 他的作用是修改SpringBoo ...
- Nginx基础知识学习(安装/进程模型/事件处理机制/详细配置/定时切割日志)
一.Linux下Nginx的安装 1.去官网 http://nginx.org/download/下载对应的Nginx安装包,推荐使用稳定版本. 2.上传Nginx到Linux服务器. 3.安装依赖环 ...
- iconv函数报错 Detected an illegal character in input string
近日使用php代码导出文件为excel,一直乱码.导出修改编码都无效,最后发现,是需要修改php导出代码本身的编码.首先用记事本打开php代码,另存为,选择ANSI格式.然后打开iconv函数这个ph ...
- UVA - 185 Roman Numerals
题目链接: https://vjudge.net/problem/UVA-185 思路: 剪枝.回溯 注意回溯的时候,是从当前点的下一个开始,而不是从已经遍历的个数点开始!!不然回溯有问题! 思路参考 ...
- service自动发现,yaml文件管理内外部端口访问
service服务发现 [root@k8s-master ~]# vim busybox-5d4f595646-dzjv4.yaml apiVersion: v1 kind: Pod metadata ...