1.怎么实现数组的?
快数组是一种线性的存储方式。新创建的空数组,默认的存储方式是快数组,快数组长度是可变的,可以根据元素的增加和删除来动态调整存储空间大小,内部是通过扩容和收缩机制实现。
慢数组是一种字典的内存形式。不用开辟大块连续的存储空间,节省了内存,但是由于需要维护这样一个 HashTable,其效率会比快数组低。
快慢数组转换:
当加入的 index- 当前capacity >= kMaxGap(1024) 时(也就是至少有了 1024 个空洞),会转变为慢数组。
处于哈希表实现的数组,在每次空间增长时, V8 的启发式算法会检查其空间占用量, 若其空洞元素减少到一定程度少于50%,则会将其转化为快数组模式。
2.v8的垃圾回收机制
调用栈中的数据:
用记录当前执行栈中的指针ESP,指针下移来销毁EC函数执行上下文。就是栈顶移除一个,出栈的过程
堆中的数据:
v8引擎将堆中的区域分为:新生代,老生代,大对象区域(体积超过其他区域的对象,每个对象都有自己的内存),代码区(代码对象,唯一拥有执行权限的内存区域),map区(cell和map,每个区域存放相同大小的元素)
新生代是副垃圾回收器在回收。老生代是主垃圾回收器在回收
副垃圾回收器分为两个区域:对象区域和空闲区域,采用scavenge算法中的Cheney,回收时交换对象区域和空闲区域来回收垃圾
副垃圾回收器里面有晋升策略,如果两次交互依然存在的话,将会将对象复制到老生代中
晋升的条件:必须经历过一次scavenge算法 空闲空间内存占比超过25%
主垃圾回收器:
主垃圾回收器会在内部构建一个根列表。采用mark sweep标记清除
标记清除就是递归遍历根结点,不能够到达就被垃圾回收,最后再将内存归还给操作系统,标记清除会造成不连续的内存碎片
所以后来诞生了mark compact 标记整理算法,他是将活动的对象放到内存的一边,解决了内存碎片的问题
V8 怎么执行 JavaScript 代码的?
其中,最核心的是三个模块:
- 解析器(Parser)
- 解释器(Ignition)
- 优化编译器(TurboFan)
当 V8 执行 JavaScript 源码时,首先解析器会把源码解析为抽象语法树(Abstract Syntax Tree),解释器(Ignotion)再将 AST 翻译为字节码,一边解释一边执行。
在此过程中,解释器会记特定代码片段的运行次数,如果代码运行次数超过某个阈值,那么该段代码就被标记为热代码(hot code),并将运行信息反馈给优化编译器(TurboFan)。
优化编译器根据反馈信息,优化并编译字节码,最终生成优化后的机器码,这样当该段代码再次执行时,解释器就直接使用优化机器码执行,不用再次解释,大大提高了代码运行效率。