Node.js的内存管理的内存管理.
一、V8的垃圾回收机制与内存限制
Node使用chrome的V8作为JS脚本引擎,因此Node的内存管理与V8关系很密切。
1.V8的内存限制
因为V8是为浏览器环境设计的,所以内存限制很小,在64位系统下约为1.4 GB,32位系统下约为0.7 GB。
2.V8的对象分配
在V8中,所有的JavaScript对象都是通过堆来进行分配的。如果已申请的堆空闲内存不够分配新的对象,将继续申请堆内存,
直到堆的大小超过V8的限制为止。
至于V8为何要限制堆的大小,表层原因为V8最初为浏览器而设计,不太可能遇到用大量内存的场景。对于网页来说,V8的限
制值已经绰绰有余。深层原因是V8的垃圾回收机制的限制。按官方的说法,以1.5 GB的垃圾回收堆内存为例,V8做一次小的
垃圾回收需要50毫秒以上,做一次非增量式的垃圾回收甚至要1秒以上。这是垃圾回收中引起JavaScript线程暂停执行的时
间,在这样的时间花销下,应用的性能和响应能力都会直线下降。这样的情况不仅仅后端服务无法接受,前端浏览器也无法接
受。因此,在当时的考虑下直接限制堆内存是一个好的选择。”
3.垃圾回收机制
V8使用分代式回收: 把内存分为新生代和老生代。 对新生代使用复制算法(内存一分二,分为From和To),对老生代使用
标记清除和标记整理算法。
在默认情况下,V8的对象分配主要集中在From空间中。对象从From空间中复制到To空间时,会检查它的内存地址来判断这
个对象是否已经经历过一次Scavenge回收。如果已经经历过了,会将该对象从From空间复制到老生代空间中,如果没有,则
复制到To空间中。另一个判断条件是To空间的内存占用比。当要从From空间复制一个对象到To空间时,如果To空间已经使
用了超过25%,则这个对象直接晋升到老生代空间中。之后From和To地位互换。
在Mark-Sweep和Mark-Compact之间,由于Mark-Compact需要移动对象,所以它的执行速度不可能很快,所以在取舍上,V8
主要使用Mark-Sweep,在空间不足以对从新生代中晋升过来的对象进行分配时才使用Mark-Compact。
增量标记
为了避免出现JavaScript应用逻辑与垃圾回收器看到的不一致的情况,垃圾回收的3种基本算法都需要将应用逻辑暂停下来,
待执行完垃圾回收后再恢复执行应用逻辑,这种行为被称为“全停顿”(stop-the-world)。
在V8的分代式垃圾回收中,一次小垃圾回收只收集新生代,由于新生代默认配置得较小,且其中存活对象通常较少,所以即
便它是全停顿的影响也不大。但V8的老生代通常配置得较大,且存活对象较多,全堆垃圾回收(full 垃圾回收)的标记、清
理、整理等动作造成的停顿就会比较可怕,需要设法改善。
为了降低全堆垃圾回收带来的停顿时间,V8先从标记阶段入手,将原本要一口气停顿完成的动作改为增量标记(incremental
marking),也就是拆分为许多小“步进”,每做完一
“步进”就让JavaScript应用逻辑执行一小会儿,垃圾回收与应用逻辑交替执行直到标记阶段完成。
V8后续还引入了延迟清理(lazy sweeping)与增量式整理(incremental compaction),让清理与整理动作也变成增量式