V8是如何执行JS的
这是自己的后期复盘,不适合初学者!
整个流程
- JS代码首先经过解析器,解析器对其进行词法分析和语法分析,将JS代码转换成AST抽象语法树。
- AST经过解释器转换成字节码,执行。解释器收集变量的类型信息以及热点函数(比如某个函数的调用多次),然后将这些信息给优化编译器,优化编译器生成机器代码,执行,速度更快。这个技术也叫运行时编译(JIT)。
- deoptimization:是当函数的参数变化的时候,编译器会告诉解释器,回退到字节码。所以我们不要将一个变量的类型改来改去的。
V8是如何垃圾回收的
整个流程
新生代
新生代采用复制互换的方法进行垃圾回收,新生代有 64MB,有两个大小一样的区域 from 和 to,首先所有变量都在 from,当 from 堆满的时候,就会将垃圾变量清除,不是垃圾的变量复制到 to 空间,然后!两者空间对调,回到最初,循环往复。
老生代
老生代采用标记清除法:V8 引擎通过从根节点开始,通过这个根节点就可以访问到所有的变量。然后进行一次广度扫描,通过引用关系,对于不是垃圾的变量标记,然后进行整理,再清除。
为什么新生代要和老生代采用不同的策略
因为新生代和老生代的大小空间不一样,新生代空间比较小,所以可以采用复制的方式,是一种典型的空间换时间的策略,但是老生代如果也采用这种策略,空间开销太大。
为什么老生代要先整理再清除
因为先整理的话,可以使用不是垃圾的变量覆盖垃圾变量的位置,后续清理的时候效率高。整理是为了解决内存碎片的问题。
内存泄漏导致的原因
- 闭包函数只使用一次,但是没有将接收变量置为null
- 定时器忘记关闭
- console.log打印的元素
- 全局变量(没有使用let或者var声明)
下面是闭包导致内存泄漏的原理
从中可以看出两个关键点:
1.函数创建的时候就确定了作用域,而且函数如果在全局中,GO和函数对象通过作用域和内存地址引用
2.函数执行过程中创建执行上下文,关联AO,确定this,arguments