V8引擎执行js的原理和垃圾回收

(一) v8执行一段js代码的过程

  1. 预解析

    检查语法错误但不生成AST

  2. 通过词法分析和语法分析生成AST(抽象语法树)

  3. AST转换为字节码

    通过V8的解释器 / 基线编译器(Ignition)将AST转换成字节码

    字节码是AST和机器码中间的过度代码,如果直接将AST转为机器码,会引发严重的内存占用问题。

  4. 由解释器逐行执行字节码,遇到热点代码启动编译器进行编译,生成对应的机器码, 以优化执行效率

(二) 垃圾回收

[1].如何判断是否可以回收

  1. 引用计数

    给一个变量赋值引用类型,则该对象的引用次数+1,如果这个变量变成了其他值,那么该对象的引用次数-1,垃圾回收器会回收引用次数为0的对象。但是当对象循环引用时,会导致引用次数永远无法归零,造成内存无法释放。

  2. 标记清除

    标记清除算法从名称上看,可以拆分为两部分:标记(mark)和清除(sweep)。

  • 1.标记阶段:垃圾回收器会从mutator(应用程序)根对象开始遍历。每一个可以从根对象访问到的对象都会被添加一个标识,于是这个对象就被标识为可到达对象。

  • 2.清除阶段:垃圾回收器,会对堆内存从头到尾进行线性遍历,如果发现有对象没有被标识为可到达对象,那么就将此对象占用的内存回收,并且将原来标记为可到达对象的标识清除,以便进行下一次垃圾回收操作。

  • 3.在使用标记清除算法时,未引用对象并不会被立即回收.取而代之的做法是,垃圾对象将一直累计到内存耗尽为止.当内存耗尽时,程序将会被挂起,垃圾回收开始执行

  1. 标记整理

    是在Mark-Sweep的基础上演变而来的

    Mark-Compact在标记完存活对象以后,会将活着的对象向内存空间的一端移动,移动完成后,直接清理掉边界外的所有内存。

[2].V8垃圾回收策略

v8采用了一种代回收的策略,将内存分为两个生代:新生代(new generation)老生代(old generation)

  1. 晋升

    对象从新生代移动到老生代的过程叫作晋升。

    1. 总结来说,如果一个对象是第二次经历从From空间复制到To空间,那么这个对象会被移动到老生代中。
    2. 当要从From空间复制一个对象到To空间时,如果To空间已经使用了超过25%,则这个对象直接晋升到老生代中。设置25%这个阈值的原因是当这次Scavenge回收完成后,这个To空间会变为From空间,接下来的内存分配将在这个空间中进行。如果占比过高,会影响后续的内存分配。
  2. 新生代

    新生代主要使用Scavenge进行管理,主要实现是Cheney算法,将内存平均分为两块,使用空间叫From,闲置空间叫To,新对象都先分配到From空间中,在空间快要占满时将存活对象复制到To空间中,然后清空From的内存空间,此时,调换From空间和To空间,继续进行内存分配,当满足那两个条件时对象会从新生代晋升到老生代。

  3. 老生代

    老生代主要采用Mark-Sweep和Mark-Compact算法,一个是标记清除,一个是标记整理。两者不同的地方是,Mark-Sweep在垃圾回收后会产生碎片内存,而Mark-Compact在清除前会进行一步整理,将存活对象向一侧移动,随后清空边界的另一侧内存,这样空闲的内存都是连续的,但是带来的问题就是速度会慢一些。在V8中,老生代是Mark-Sweep和Mark-Compact两者共同进行管理的。

https://juejin.im/post/6844903591510016007#heading-7

猜你喜欢

转载自blog.csdn.net/kouzuhuai2956/article/details/112007747