2020-08-28-Node内存控制

V8内存

V8内部的内存对象分为新生代和老生代,新生代是代表存在时间较短,很快被释放内存空间的对象,而老生代对象则是常驻在Node进程中,
只有进程结束才会被销毁。

一般来说V8对新老生代对象的内存分配是固定的,根据设备的64位(old: 1400MB,new: 4*16MB)或者32位(减半)分配对应的内存。
但是我们也可以在node进程启动的时候主动去分配。

node --max-old-space-size=1700 test.js // 单位为MB
node --max-new-space-size=1024 test.js // 单位为KB 

垃圾回收机制:

分代式垃圾回收

  • scavenge算法(复制剔除大法)

    主要针对新生代对象的垃圾回收,它会将新生代分配的内存拆分为两份(from-to),新晋的新生代对象分配到from区,当进行垃圾回收的
    时候,会将from区的对象复制到to区,在这个过程中,判断出可以销毁的变量对象,直接释放对应的内存,然后再将to区的对象还原到from区,
    等待下次回收。

    这个算法相当于只能利用一半的内存,但是速度非常快,典型的空间换时间,空间还是比较宝贵的,所以
    scavenge算法只适合用于新生代小对象的回收。

  • 晋升

    在经历过scavenge处理多次后,仍然处于存活状态的对象,就会被移动到老生代中,采用新的算法进行管理。

    触发条件:

    1、经过scavenge回收;

    根据对象的内存地址来判断是否进行过scavenge回收,有,则进入老生代空间。

    2、To空间内存占用超过限制(新生代内存分配较少)。

    在复制到To空间的过程中判断To空间的使用率是否超过25%,是,则进入老生代空间。
    之所以限制25%,因为回收完毕后,To空间会与From空间进行互换,如果占比过高,会影响From空间的新的内存分配。

  • mark-sweep算法(标记清除大法)

    对于老生代对象,大部分对象的存活期是比较长的,频繁的进行复制操作,非常耗时,所以采用mark-sweep算法来进行老生代对象的回收,
    它遍历整个堆中的对象,标记出存活和死亡的对象,然后将死亡的对象内存空间直接释放。

    该算法的问题是回收完毕后,会出现内存空间不连续,产生内存碎片,影响后续大对象的内存分配。

  • mark-compact(标记清除再聚拢)

    mark-sweep的衍生版本,在清除死对象的时候,将存活对象向一端移动,避免产生内存碎片。

算法          scavenge        mark-sweep      mark-compact
---------------------------------------------------------
空间开销    双倍空间(无碎片)    少(有碎片)      少(无碎片)
速度          极快              中等              最慢
是否移动对象    是                否                是

内存溢出

通常来说内存溢出的原因有:

  • 作用域未被及时释放(闭包、过多的全局变量等)
  • 缓存(未设置过期时间)
  • 队列消费不及时(定时器或异步操作导致事件队列堆积了过多的任务)

大文件操作

由于V8内部对内存大小有限制,所以在读取一些超大文件时,我们无法通过fs模块,而需要使用Stream流,它分为可读、可写流,
含有pipe方法,对文件流进行操作,不会受到内存的影响。

var reader = fs.createReadStream('a.txt');
var writer = fs.createWriteStream('b.txt');
reader.pipe(writer);

你可能感兴趣的:(2020-08-28-Node内存控制)