12 | 栈空间和堆空间:数据是如何存储的?
这节讲解的是JavaScript的内存机制。
首先,我们知道JavaScript是弱类型动态语言。
接着,JavaScript的数据类型一共是八种:Boolean| String | Number | Undefined | Null | BigInt | Symbol | Object
前七种为基本数据类型,他们存在栈中,后一种为引用数据类型,它存在堆中。
13 | 垃圾回收:垃圾数据是如何自动回收的?
不同语言的垃圾回收策略
- 通常情况下,垃圾回收分为手动回收和自动回收两种策略。
- 手动垃圾回收:例如C++,何时分配内存、何时销毁是由代码控制的。
- 自动垃圾回收:JavaScript、Java、Python等,产生的垃圾数据是由垃圾回收器释放的。
调用栈中的数据是如何回收的
当一个函数执行完毕,JavaScript引擎会通过向下移动ESP(记录当前执行状态的指针)来销毁该函数保存在栈中的执行上下文。
堆中的数据是如何回收的
要回收堆中的垃圾数据,就需要用到JavaScript中的垃圾回收器了。
代际假说
代际假说(The Generaltional Hypothesis),是垃圾回收领域一个重要的术语,后续垃圾回收策略都是建立在该假说的基础上的。
特点一:大部分对象在内存中的时间很短,简单来说,很多对象一经分配内存,很快就变得不可访问。
特点二:不死的对象,会活的更久。
堆中区域
V8会把堆分为新生代和老生代两个区域。新生代存放的是生存时间短的对象,老生代存放的是生存时间久的对象。
堆中的这两块区域,V8分别使用两个不同的垃圾回收器,以便高效的实施垃圾回收。
副垃圾回收器,主要负责新生代区域的垃圾回收。
主垃圾回收器,主要负责老生代区域的垃圾回收。
垃圾回收器工作流程
- 标记空间中的活动对象和不活动对象。
- 回收非活动对象所占据的内存。
- 内存整理(主垃圾回收器产生的内存碎片会导致出现不连续内存空间,所以需要整理)
副垃圾回收器
- 负责新生代区域的垃圾回收
- 大多数小的对象会分配至此。
- 新生代用Scavenge算法处理—》一半对象区域,一半空闲区域。
- 存活的对象放入对象区域,然后垃圾清理阶段,将对象区域中活跃对象复制至空闲区域,完成复制后,两个区域进行角色转换。
- 经过两次垃圾回收依然还存活的对象,会被移到老生区中。
主垃圾回收器
- 负责老生代区域的垃圾回收。
- 除了新生区中移过来的对象,一些大对象会被直接分配到老生区
- 老生区对象的特点:存活时间长,占用内存空间大。
- 主垃圾回收器采用的是标记-清除的算法进行垃圾回收。
全停顿
当执行垃圾回收算法的时候,JS脚本会暂停下来,这种行文叫做全停顿。
为了降低全停顿的卡顿影响,V8通过增量标记算法将完整的垃圾回收任务分为一个个小任务,并与JS脚本交替执行。
14 | 编译器和解释器:V8是如何执行一段JavaScript代码的?
了解V8是如何执行一段JavaScript代码,可以让我们对babel、EsLint、Vue、React等其底层实现机制有一个更充分的认识。
主要就是搞懂这些概念和原理:编译器(Compiler)、解释器(Interpreter)、抽象语法树(AST)、字节码(Bytecode)、即时编译器(JIT)等。
编译器和解释器
首先我们必须得知道编译型语言和解释型语言的区别。
编译型语言就是程序执行前,编辑器经过编译,成二进制文件,程序可直接运行二进制文件。如C/C++、Go
解释器语言是在运行时动态解释和执行。如JS、Python等。
编译器、解释器执行流程
编译型语言的编译过程:
- 编译器对源代码进行词法分析、语法分析,生成抽象语法树(AST)
- 优化代码,生成机器码、二进制文件。
- 编译成功,生成可执行文件。编译失败抛出异常。
解释型语言解释过程:
- 解释器对源代码进行词法分析、语法分析,生成抽象语法树(AST)
- 基于抽象语法树生成字节码。
- 根据字节码执行程序、输出结果。
V8是如何执行一段JavaScript代码的
V8执行过程中,既有解释器又有编译器。其执行流程为:
1. 生成抽象语法树(AST)和执行上下文
将源代码转换成抽象语法树,并生成执行上下文。
过程就是这么个过程,但是抽象语法树的理解我们要展开。
- 无论编译型语言还是解释型语言,都会将源代码生成AST。
- AST的结构与代码结构类似。
(Babel工作原理就是:ES6源码->ES6AST->ES5AST->ES5源码)- 生成ATS经过两个阶段:
- 第一阶段是分词(词法分析),将一行行源码拆解成不可再分的最小当个字符或字符串token。
- 第二阶段是解析(语法分析),将token根据语法规则生成AST
2.生成字节码
解释器登场,根据AST生成字节码、解释字节码。
一开始V8没有字节码,之前是直接生成机器码,这样的效率很高,但是很占内存,随着移动互联网的发展,为了解决内存占用过高问题,而引入了字节码,即字节码的内存占用要比机器码小很多。
字节码是介于AST和机器码之间的一种代码,它需要通过解释器将其转换为机器码后才能执行。
3.执行代码
解释器除了生成字节码,还要解释字节码,后台编译器会把热点字节码编译成高效的机器码,这种字节码配合解释器和编译器的技术,就叫做即时编译(JIT).使用这种技术的除了V8还有Java和Python虚拟机等都使用了。