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。

新生代内存实现

  1. 新生代将内存空间一分为二,分为From(表示内存正在使用的内存),To(目前闲置的内存)。
  2. 当进行垃圾回收时,v8将From部分的对象检测一遍,如果是存活对象那么复制到To内存中(在To内存中按照顺序从头放置),如果是非存活对象直接回收即可。
  3. 当所有的From中的存活对象按照顺序进入到To内存之后,From和To两者的角色对调,From变为闲置,To变为正在使用,如此循环
  4. 注意:不直接回收非存活对象是为了避免内存碎片的产生,在From到To的过程中将存活对象连续分配,增加空间利用率。这种算法叫Scavenge算法

老生代内存的回收

新生代中的变量如果经过多次回收依然存在,就会被放入到老生代内存中,这种现象叫晋升。 触发晋升的原因有:

  1. 已经经历过一次Scavenge回收
  2. To(闲置)空间的内存占用超过25%

老生代回收实现

  1. 进行标记-清除,分为标记阶段和清除阶段。首先遍历堆中所有对象,对其做上标记,然后对于代码环境中使用的变量以及被强引用的变量取消标记,然后在清除阶段将还在标记的变量删除,进行空间的回收。
  2. 整理内存碎片。在清除阶段结束后,把存活的对象全部往一端靠拢。由于移动对象,他的执行速度很慢,也是整个过程最耗时的部分。

增量标记

由于JS的单线程机制,v8在进行垃圾回收的时候,不可避免会阻塞逻辑的执行,倘若老生代的垃圾回收任务很重,那么耗时就非常可怕严重影响性能。

v8采用增量标记,即将一次完成的标记任务分为很多小的部分完成,每完成一个小的部分就休息,去执行一段js应用逻辑,再执行下面的标记,如此循环知道标记阶段完成。

经过增量标记后,垃圾回收过程对JS应用的阻塞时间减少到原来1/6。

三色标记

目前v8用的标记方法是三色标记,摒弃了原先的增量标记,因为增量标记是片段执行的,忽略了标记扫描的过程,再下一次片段标记时无法知道上一次标记的位置。

三色标记解决了增量标记中传统双色标记过程无法分片的问题,将需要扫描但是还未扫描的设置为一色(例如灰色),需要扫描且已经扫描的设置为一色(例如黑色),不需要扫描的设置为一色(例如白色)。

results matching ""

    No results matching ""