v8引擎垃圾内存回收
JS采用了一套自己的自动内存管理,会自动为我们处理内存空间,并优化
v8内存限制
- v8只能分配系统的一部分内存,在64位系统中最多分配
1.4G
,在32位系统中最多分配0.7G
- 在JS中所有的对象类型的数据都是通过堆进行空间分配的。当我们构造一个对象进行赋值操作时,其实相应的内存已经分配到了堆上。可以不断的分配空间,知道堆的大小达到上限。
- 设置内存上限的原因是由JS单线程的执行机制和JS垃圾回收机制的限制共同决定的。
- JS的单线程运行意味着一旦进入到垃圾回收,那么其它的各种运行逻辑就会暂停。
- 其次,垃圾回收其实是非常耗时的操作,导致应用卡顿,性能和响应能力下降,所以v8通过限制堆内存来避免操作几个G的情况。
- 还是可以调整内存的限制,配置如下
// 调整老生代部分的内存,单位是MB
node --max-old-space-size=2048 xxx.js
// 调整新生代部分的内存,单位是KB
node --max-new-space-size=2048 xxx.js
新生代内存的回收
v8将堆内存分为两部分——新生代内存和老生代内存。新生代是临时分配的内存(存活时间短,分配的内存较小),老生代是常驻内存(存活时间长)。v8的堆内存就是新老生代内存和。
在64位和32位系统下,新生代分配的内存分别为32MB和16MB。
新生代内存实现
- 新生代将内存空间一分为二,分为From(表示内存正在使用的内存),To(目前闲置的内存)。
- 当进行垃圾回收时,v8将From部分的对象检测一遍,如果是存活对象那么复制到To内存中(在To内存中按照顺序从头放置),如果是非存活对象直接回收即可。
- 当所有的From中的存活对象按照顺序进入到To内存之后,From和To两者的角色
对调
,From变为闲置,To变为正在使用,如此循环 - 注意:不直接回收非存活对象是为了避免内存碎片的产生,在From到To的过程中将存活对象连续分配,增加空间利用率。这种算法叫Scavenge算法
老生代内存的回收
新生代中的变量如果经过多次回收依然存在,就会被放入到老生代内存
中,这种现象叫晋升
。
触发晋升的原因有:
- 已经经历过一次Scavenge回收
- To(闲置)空间的内存占用超过25%
老生代回收实现
- 进行标记-清除,分为标记阶段和清除阶段。首先遍历堆中所有对象,对其做上标记,然后对于代码环境中
使用的变量
以及被强引用
的变量取消标记,然后在清除阶段
将还在标记的变量删除,进行空间的回收。 - 整理内存碎片。在清除阶段结束后,把存活的对象全部往一端靠拢。由于移动对象,他的执行速度很慢,也是整个过程最耗时的部分。
增量标记
由于JS的单线程机制,v8在进行垃圾回收的时候,不可避免会阻塞逻辑的执行,倘若老生代的垃圾回收任务很重,那么耗时就非常可怕严重影响性能。
v8采用增量标记,即将一次完成的标记任务分为很多小的部分完成,每完成一个小的部分就休息,去执行一段js应用逻辑,再执行下面的标记,如此循环知道标记阶段完成。
经过增量标记后,垃圾回收过程对JS应用的阻塞时间减少到原来1/6。
三色标记
目前v8用的标记方法是三色标记,摒弃了原先的增量标记,因为增量标记是片段执行的,忽略了标记扫描的过程,再下一次片段标记时无法知道上一次标记的位置。
三色标记解决了增量标记中传统双色标记过程无法分片的问题,将需要扫描但是还未扫描的设置为一色(例如灰色),需要扫描且已经扫描的设置为一色(例如黑色),不需要扫描的设置为一色(例如白色)。