Skip to main content

V8工作原理

栈空间和堆空间

JS 是一门弱类型、动态的语言

  • 弱类型:意味着不需要告诉js引擎数据类型,它会自动判断
  • 动态:同一个变量可以保存不同类型的数据

内存空间

js 内存模型包含:代码空间、栈空间、堆空间

  • 原始类型是直接保存在栈空间中的,会随着当前栈元素的弹出而销毁
  • 引用类型的值是存在堆空间
  • 栈空间都不会设置太大,主要用来存放一些原始类型的小数据

  • 堆空间很大,能存放很多大的数据

在谈闭包

代码同上一模块的闭包

  1. 当JavaScript引擎执行到foo函数时,首先会编译,并创建一个空执行上下文。
  2. 在编译过程中,遇到内部函数setName,JavaScript引擎还要对内部函数做一次快速的词法扫描,发现该内部函数引用了foo函数中的myName变量,由于是内部函数引用了外部函数的变量,所以JavaScript引擎判断这是一个闭包,于是在堆空间创建换一个“closure(foo)”的对象(这是一个内部对象,JavaScript是无法访问的),用来保存myName变量。
  3. 接着继续扫描到getName方法时,发现该函数内部还引用变量test1,于是JavaScript引擎又将test1添加到“closure(foo)”对象中。这时候堆中的“closure(foo)”对象中就包含了myName和test1两个变量了。
  4. 由于test2并没有被内部函数引用,所以test2依然保存在调用栈中。

垃圾回收机制

调用栈的数据回收

通过向下移动ESP来销毁该函数保存在栈中的执行上下文

堆的回收

将会用到垃圾回收器

代际假说

  • 大部分对象在内存中存在的时间很短,
  • 不死的对象会活得更久

新生代垃圾回收器(副垃圾回收器)

Scavenge算法:新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域

  • 对象区域快被写满时,就需要执行一次垃圾清理
  • 对对象区域中的垃圾做标记
  • 标记完成之后,就进入垃圾清理阶段,把这些存活的对象复制到空闲区域,并排列起来(内存整理)
  • 完成复制后,对象区域与空闲区域进行角色翻转

为了执行效率,一般新生区的空间会被设置得比较小

因为新生代空间小,容易存满,所以有 对象晋升策略

经过两次垃圾回收依然还存活的对象,会被移动到老生区中。

老生区(主垃圾回收器)

主垃圾回收器是采用标记-清除(Mark-Sweep)的算法进行垃圾回收

  1. 标记阶段从一组根元素开始,递归遍历,能达到的标记为活动对象,达不到的标记为垃圾数据
  2. 将标记为垃圾数据的清除
  3. 由于清除导致内存不连续,所以标记-整理(Mark-Compact)

全停顿

当对大量数据进行回收时,会造成整个主线程阻塞,所以增量标记(Incremental Marking)算法

编译器和解释器

编译型语言:c/c++ 、go

解释型语言:python、js

V8 是如何执行一段 js 代码的

V8在执行过程中既有解释器Ignition,又有编译器TurboFan

步骤

  • 词法语法分析,生成抽象语法树(AST)和执行上下文

    • 分词(tokenize),即词法分析
    • 解析(parse),即语法分析
  • 生成字节码

    字节码就是介于AST和机器码之间的一种代码。

    占用空间远远小于机器码

  • 执行代码

    即时编译(JIT)