Node内存限制与解决方案

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node内存限制与解决方案相关的知识,希望对你有一定的参考价值。

参考技术A 导致的问题: Node 无法直接操作大文件对象。

例如我想读取一个 4g 的文件来处理,即使物理内存有 32GB,在单个 Node 进程中也是不能完全的使用的。

我们平常在声明一些对象的时候,要是没有Node垃圾回收机制回收 ,就会占用V8限制的内存

内存限制主要原因是v8的垃圾回收制度。1.5GB内存做一次小的回收需要50MS,做一次非增量性回收需要1S以上,并且这会使JS线程暂停。因此限制内存。

所有的 JS 对象都是通过堆来进行分配的。

可以使用Buffuer,因为Buffer不受V8的内存分配机制,

Node.js程序所使用的内存分为两类:

在程序允许的情况下,应该将数据保存在 Buffer 中,而不是转换成字符串等JS对象,这样可以避免 V8 内存的过多占用。

process.nextTick()会在本次事件循环结束后,立即开始下次事件循环。这样可以使V8获得内存回收的机会,有效解决过多事件堆积造成的内存溢出。

我们可以使用process.nextTick()方法处理:

每次循环V8都会回收内存一次,因此内存不会再溢出。但这样做必然会造成运行效率的降低,而应该在速度在安全之间平衡,控制好循环的安全次数。

官方建议:it is recommended that you split your single process into several workers if you are hitting memory limits. (拆分进程)

Node.js——nodejs(内存控制)(转)

node使用V8作为javaScript脚本引擎

v8的内存限制和对象分配

限制:64为大约1.4G,32位大约0.7G
v8中所有javascript对象都是通过堆内存进行分配的。内存查看命令process.memoryUsage()

为何要内存限制

表层原因为v8最初为浏览器设计,不太可能遇到大量的内存的场景。对于网页来说,v8的限制已经绰绰有余,深层原因是v8的垃圾回收机制的限制.
v8打开堆内存的限制命令 --max-old-space-size或--max-new-space-size

v8的垃圾回收机制

v8的内存分代

主要将内存分为新生代和老生代。新生代中为存活时间交短的对象,老生代中的对象为存活时间较长或者常驻内存对象

  • 新生代内存在64位系统中为16MB,32位中为8MB
  • v8堆内存最大保留空间 4 * 新生内存 + 老生代内存

新生代内存垃圾回收

采用一种复制方式的垃圾回收算法,将堆内存一分为二,只有一部分空间被使用称为From空间,另一个处于闲置称为To空间。当进行分配对象的时候先在from空间分配,当进行垃圾回收时,会检查from空间中的存活对象,将这些存活对象复制到to空间中,复制完成后From和to空间角色互换,清空to空间,在垃圾回收过程中就是通过将存活对象在两个空间中进行复制。

  • 缺点: 只能使用一半的内存
  • 优点: 只复制存活的对象,对于生命周期短的场景存活对象只占小部分,所以时间效率高
    当一个对象经过多次复制依然存活时,就会被认为是生命周期较长的对象,会被移入老生代内存中。
    对于移入老生代内存有两个条件:
  • 对象已经经过新生代内存回收机制的回收依然存活
  • 复制到To空间的对象超过25%(为什么是25%?这个To空间接下来会成为From空间并接受内存分配,如果占比过高影响后续分配)

老生代内存垃圾回收

采用标记清除,它分为标记清除两个阶段
在标记阶段遍历所有的对象并标记活着的对象,在清除阶段只清除死亡的对象,死亡对象在老生代内存只占一小部分。老生代内存进行一次清除后,内存空间会出现不连续的状态,所以清理完成需要进行一步标记整理。

为了避免出现javaScript应用逻辑与垃圾回收器看到不一致的情况,垃圾回收都要将应用逻辑停下来,这种行为会造成停顿,在新生代垃圾回收过程中因为存活对象比较少,即使停顿基本影响不大。在老生代垃圾回收中,通常存活对象较多,全堆垃圾回收的标记、清除、整理影响较大。
解决办法:分批次进行,拆分成许多小步,每进行一小步就让逻辑运行一会

node 查看垃圾回收日志 运行时加入 参数 --trace_gc

高效使用内存

作用域

减少使用全局作用域,在局部作用域声明变量。当函数执行完成,该作用域就会被销毁,只别局部变量引用的对象存活较短,会被分配到新生代内存,方便回收

闭包

function test() {
   var a = 1
   return function () {
      return a
  }  
}
var fn1 = test() 

堆外内存

通过命令process.memoryUsage() 可以查看代rss总是大于常驻内存总量,在node中并不是所有的内存都通过v8进行分配,不通过v8进行分配的内存称为堆外内存,通过Buffer 分配的内存即为堆外内存,所有处理大量数据的时候可以使用Buffer 进行分配

内存泄漏

通常造成内存泄漏的原因有:

  • 缓存
  • 队列消费不及时
  • 作用域未释放

慎用内存当缓存

缓存的访问效率要比I/O的效率高很多,一旦命中缓存,就可以节省一次I/O的时间,但是在node中,一旦一个对象被当作缓存,那它将常驻老生代内存,缓存中储存的键越多,长期存活的对象就越多,这将导致垃圾回收在进行扫描和整理是频繁的多这些对象做无用功。

 

以上是关于Node内存限制与解决方案的主要内容,如果未能解决你的问题,请参考以下文章

限制 node.js 的内存使用

Node.js——nodejs(内存控制)(转)

Node.js(和 chrome V8)中的内存限制

Node 内存控制

内存回收机制

深入浅出Node.js - 内存控制