V8 介绍
V8 是市面上主流得 JS 执行引擎,它采用即时编译得运行方式,提高了编译效率。它设有一个内存上限。在 64 位运行环境下,不超过 1.5G, 在32位环境下不超过 800M,在浏览器来说,再加上垃圾回收机制,这个内存大小是完全够用的,同时,如果空间过大的话,在垃圾回收过程中耗费的时间可能会被用户明显的感知到,造成卡顿。
V8垃圾回收策略
首先 v8 中采用了分代回收的思想,所谓分代回收即是把数据分成新生代和老生代,然后根据不同的对象采取不同的办法。操作如 图5.1 。
新生代对象指那些存活时间较短的数据,如块作用域内的那些变量,老生代对象指的是存活时间较长的数据,如全局变量那些。
所以 V8 的内存空间分为两部分,较小的一部分用来存储新生代对象, 64x 环境下大小为 32M ,32x 环境下,大小为 16M 。较大的一部分用来存储老生代对象, 64x 环境下大小为 1.4G ,32x 环境下,大小为 700M 。
新生代回收过程:
在新生代的回收过程中主要采用了空间复制和标记整理算法。首先新生代的存储空间被分为两个等大的空间 From 和 To 。当变量申请空间时,全去会申请在 From 空间内,让后对其进行标记整理,使得空间连续,避免浪费。然后把整理好的内容复制到 To 空间。最后让 From 空间和 To 空间进行交换并释放掉 From 空间的内容。然后把 From 空间可看作 To 空间,把 To 空间可看作 From 空间。
在空间复制过程中可能会存在一个晋升的操作,晋升也就是从新生代对象移动至老生代。能触发晋升操作的有两种情况。1. 一轮 GC 操作之后还存在的变量。 2. 当 To 空间的占用率超过 25% 时候,因为 To 空间 的占用率过多的话,当它变为 From 空间时可能无法存入新的变量。
老生代回收过程:
在老生代的回收过程中主要使用了标记清除,标记整理,标记增量。
在回收过程中主要采用了标记清除算法,这样操作对于性能速度的提升非常明显。当晋升操作时,且老生代存储空间不足时,就会触发一次标记整理。以获取更大的连续空间。最后采用标记增量的方法来进行效率提升。
所谓标记增量也就是把一次标记拆分成多次标记,因为 JS 的执行,和垃圾回收的过程是不可以同时进行的,如果一次标记内容过多,用户的体验必然不好。
同时可达对象不一定是第一层就可以访问到的,可能需要多层递进的访问,所以我们可以等程序执行到某一处引用时,然后对其进行标记。这样进行多次标记,给用户更好的体验。如 图5.2 所示,
两者回收相比之下,新生代的思想是用空间换时间,基本上近半的空间不会用到,但是它的空间本就小,所以浪费一些可以接受。而且因为小,所以复制时候压力就小。 老生代的空间大,如果同新生代一样一分为二,不仅浪费空间难以承受,而且复制压力大。