(一) v8执行一段js代码的过程
-
预解析
检查语法错误但不生成AST
-
通过词法分析和语法分析生成
AST
(抽象语法树) -
将
AST
转换为字节码
通过V8的解释器 / 基线编译器(Ignition)将AST转换成字节码
字节码是AST和机器码中间的过度代码,如果直接将AST转为机器码,会引发严重的内存占用问题。
-
由解释器逐行执行
字节码
,遇到热点代码启动编译器进行编译,生成对应的机器码, 以优化执行效率
(二) 垃圾回收
[1].如何判断是否可以回收
-
引用计数
给一个变量赋值引用类型,则该对象的引用次数+1,如果这个变量变成了其他值,那么该对象的引用次数-1,垃圾回收器会回收引用次数为0的对象。但是当对象循环引用时,会导致引用次数永远无法归零,造成内存无法释放。
-
标记清除
标记清除算法从名称上看,可以拆分为两部分:标记(mark)和清除(sweep)。
-
1.标记阶段:垃圾回收器会从mutator(应用程序)根对象开始遍历。每一个可以从根对象访问到的对象都会被添加一个标识,于是这个对象就被标识为可到达对象。
-
2.清除阶段:垃圾回收器,会对堆内存从头到尾进行线性遍历,如果发现有对象没有被标识为可到达对象,那么就将此对象占用的内存回收,并且将原来标记为可到达对象的标识清除,以便进行下一次垃圾回收操作。
-
3.在使用标记清除算法时,未引用对象并不会被立即回收.取而代之的做法是,垃圾对象将一直累计到内存耗尽为止.当内存耗尽时,程序将会被挂起,垃圾回收开始执行
-
标记整理
是在Mark-Sweep的基础上演变而来的
Mark-Compact在标记完存活对象以后,会将活着的对象向内存空间的一端移动,移动完成后,直接清理掉边界外的所有内存。
[2].V8垃圾回收策略
v8采用了一种代回收的策略,将内存分为两个生代:新生代(new generation)和老生代(old generation)。
-
晋升
对象从新生代移动到老生代的过程叫作晋升。
- 总结来说,如果一个对象是第二次经历从From空间复制到To空间,那么这个对象会被移动到老生代中。
- 当要从From空间复制一个对象到To空间时,如果To空间已经使用了超过25%,则这个对象直接晋升到老生代中。设置25%这个阈值的原因是当这次Scavenge回收完成后,这个To空间会变为From空间,接下来的内存分配将在这个空间中进行。如果占比过高,会影响后续的内存分配。
-
新生代
新生代主要使用Scavenge进行管理,主要实现是Cheney算法,将内存平均分为两块,使用空间叫From,闲置空间叫To,新对象都先分配到From空间中,在空间快要占满时将存活对象复制到To空间中,然后清空From的内存空间,此时,调换From空间和To空间,继续进行内存分配,当满足那两个条件时对象会从新生代晋升到老生代。
-
老生代
老生代主要采用Mark-Sweep和Mark-Compact算法,一个是标记清除,一个是标记整理。两者不同的地方是,Mark-Sweep在垃圾回收后会产生碎片内存,而Mark-Compact在清除前会进行一步整理,将存活对象向一侧移动,随后清空边界的另一侧内存,这样空闲的内存都是连续的,但是带来的问题就是速度会慢一些。在V8中,老生代是Mark-Sweep和Mark-Compact两者共同进行管理的。
https://juejin.im/post/6844903591510016007#heading-7