node内存基础知识

1. node内存分配基础

在V8中所有的JavaScript对象都是通过堆来分配的。为了提高垃圾回收的效率,V8将堆分为新生代和老生代两个部分,其中新生代为存活时间较短的对象(需要经常进行垃圾回收),而老生代为存活时间较长的对象(垃圾回收的频率较低)。

新生代和老生代的默认内存限制在启动的时候就确定了,没办法根据应用使用内存情况自动扩充,当应用分配过多内存时,就会引起OOM(Out Of Memory,内存溢出)进程错误。64位系统和32位系统的内存限制不同,分别如下:

类型 64位系统 32位系统
新生代 32MB x 2 16MB x 2
老生代 1400MB 700MB
实际可用内存 1432MB 716MB

在node启动时,通过--max-new-space-size--max-old-space-size可分别设置新生代和老生代的默认内存限制。V8为什么要对内存做如此限制呢?最终的原因还是V8的垃圾回收机制所限制的,在较大的内存上进行垃圾回收是很耗时地。下面我们就来了解一下V8的垃圾回收机制。

2. node垃圾回收原理

2.1 常用垃圾回收基本算法

垃圾回收机制有多种,但最常用的就是以下几种:

类型 方法 是否停止程序
引用计数(Reference Counting) 每个对象配置一个计数器即可,每当引用它的对象被删除时,就将其引用数减1,当其引用计数为0时,即可清除
标记-清除(Mark-Sweep) 标记阶段标记活对象,清除阶段清除未被标记的对象
停止-复制(Stop-Copy) 内存分为两块,并将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色
标记-压缩(Mark-Compact) 对所有可达对象做一次标记,将所有的存活对象压缩到内存的一端,以减少内存碎片
增量算法(Incremental Collecting) 每次,垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程。依次反复,直到垃圾收集完成。

2.2 V8的分代垃圾回收

上面提到过,V8将内存分为新生代和老生代,新生代中对象存活时间较短,老生代中对象存活时间较长。为了最大程度的提升垃圾回收效率,V8使用了一种综合性的方法,其在新生代和老生代中分别使用上文提到的不同的基本垃圾回收算法。

[1] 新生代垃圾回收算法Scavenge

在新生代中,由于内存较小(64位系统为64MB)且存活对象较少,V8采取了一种以空间换时间的方案,即停止-复制算法 (Stop-Copy)。它将新生代分为两个半区域(semi-space),分别称为from空间和to空间。一次垃圾回收分为两步:

(1) 将from空间中的活对象复制到to空间 (2) 切换from和to空间

V8将新生代中的一次垃圾回收过程,称为Scavenge。

[2] 老生代垃圾回收算法

老生代的内存空间较大且存活对象较多,因此其垃圾回收算法也就没有新生代那么简单了。为此V8使用了标记-清除算法 (Mark-Sweep)进行垃圾回收,并使用标记-压缩算法 (Mark-Compact)整理内存碎片,提高内存的利用率。老生代的垃圾回收算法步骤如下: