# V8 引擎
# V8 垃圾回收
JavaScript 原始类型数据存放在栈中,通过移动栈顶指针 ESP 实现变量销毁,天生不需要垃圾回收。对象类型数据以及闭包空间中的数据存放在堆中,堆中的数据需要垃圾回收。
代际假说 (The Generational Hypothesis) 表明一个对象的生命周期要么很短要么很长。因此 V8 引擎实现了两种不同的垃圾回收器。
- 副垃圾回收器负责回收新生代对象
- 主垃圾回收器负责回收老生代对象
生命周期 | 体积大小 | |
---|---|---|
新生代对象 | 短 | 小 |
老生代对象 | 长 | 大 |
垃圾回收器执行流程:
- 标记非活动对象
- 回收非活动对象的内存
- 进行内存碎片整理
V8 副垃圾回收器采用 Scavenge 算法将新生区内存分为对象区域和空闲区域,两个区域轮换使用。轮换时将一个区域的活动对象复制到另一个区域,天生不会产生内存碎片。由于新生代对象体积小,所以也不会浪费太多内存。
主垃圾回收器采用 Mark-Sweep 算法。由于会产生内存碎片,因此派生出了 Mark-Compact 算法。由于老生区空间很大,一次完整回收会卡顿很久,因此派生出了 Incremental Marking 算法。
JavaScript 中分配的大对象会直接存放在老生区。小对象会先放在新生区,如果经历了两次垃圾回收还存活则会被移动到老生区,这个过程叫做对象晋升。
# V8 编译器/解释器
一些基本的编译原理常识这里就不介绍了:
- 词法分析 (tokenize)
- 语法分析 (parse)
- 抽象语法树 (AST)
- 编译和解释的输入都是 AST
- 编译的输出是:中间代码、二进制文件/机器码
- 解释的输出是:字节码
- 相比字节码来说,机器码占用空间大,但执行效率高
即时编译 (JIT),是解释器和编译器配合的一种技术。在 V8 中,AST 先全部转换为字节码,Ignition 在解释执行字节码的同时,收集代码信息,当它发现某一部分代码变热了之后,TurboFan 便会把热点代码 (HotSpot) 转换为机器码。这样就实现了内存空间和执行效率的平衡。