在node.js中,内存主要分为两个部分,堆内存和栈内存
- 堆内存(heap):存放对象和闭包上下文,v8使用垃圾回收机制管理堆内存
- 栈内存(stack):存放局部变量,栈内存的分配比较简单,当程序离开某作用域后,其栈指针下移(回退),整个作用域的局部变量都会出栈被回收。
可通过process.memoryUsage()查看内存使用情况,单位为字节
- rss是分配的整体物理内存,包括堆、栈、代码段
- heapTotal是整体堆内存
- heapUsed是被使用的堆内存
- external: 代表v8管理的绑定到javascript的c++对象的内存
通常情况下,v8对堆内存的分配为,64位系统下1.4GB,32位系统下0.7GB
node将堆内存分为新生代和老生代
- 新生代:存放存活时间较短的对象
- 老生代:存放存活时间较长的对象
在分代的基础上,对于新生代使用Scavenge算法,老生代使用Mark-Sweep和Mark-Compact
- 对象已经经历过一次复制翻转操作。
- To空间使用比例超过25%,设置25%的比例是因为,To空间将变成From空间,如果使用比例过高,可能影响后续的内存分配。
Scavenge算法
Scavenge算法主要通过Cheney算法实现,Cheney算法将新生代分为两部分,叫做SemiSpace空间,该两个SemiSpace空间分别叫做From空间和To空间。
From空间为使用空间,To空间为闲置空间。分配新的内存空间时会分配到From空间。开始进行垃圾回收时,会检查From空间内的对象,将存活对象复制至To空间,释放From空间,然后将From空间和To空间进行翻转。
Scavenge算法的优点是速度快,无内存碎片,缺点是,只能使用一般的内存空间,但是由于新生代都是存活时间较短的对象,需要复制的对象属于少数。属于使用空间换时间的典型算法。只适用于新生代垃圾回收策略。
在新生代回收阶段,如果存活对象满足以下两个条件,会将该对象移动至老生代,该阶段叫做晋升
- 对象已经经历过一次复制翻转操作。
- To空间使用比例超过25%,设置25%的比例是因为,To空间将变成From空间,如果使用比例过高,可能影响后续的内存分配。
Mark-Sweep(标记清除)和Mark-Compact(标记整理)
Mark-Sweep分为标记和清除两个阶段,在标记阶段遍历所有老生代对象,对还存活的对象进行标记,在清除阶段释放未被标记的内存。
Mark-Sweep最大问题是,经过清除阶段后,内存空间会出现不连续状态,影响以后的内存分配。可能出现一个对象,需要分配内存空间很大,而现有内存碎片不能完成分配,进行提前垃圾回收,该回收是没有必要的。
为了解决Mark-Sweep的内存碎片问题,Mark-Compact被提了出来,它主要是在标记之后,将存活的对象往一侧移动,完成移动后,直接释放边界外的内存。
Incremental Marking
该三种算法执行时,为了保证javascript应用逻辑与垃圾回收机制保持一致,垃圾回收时都需要将应用逻辑停止,这种情况称之为“全停顿”
新生代Scavange算法,由于分配内存空间较小,存活对象较少,全停顿影响较少。
但是老生代内存空间较大,存储对象较多,全停顿时间较长。所以V8为了减少停顿空间,从标记阶段开始,改为增量标记(Incremental Marking)。将整体拆分为小“步进”,没执行一个“步进”就让js应用逻辑执行一会儿, 垃圾回收与应用逻辑交替进行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。