node 的内存控制

博客分类: 原创

node 的内存控制

谈笑风生那可遇不可求的事情可以说是温文尔雅的形容了吹牛逼一说。其实和我写这篇博客差不多。标题前端看起来高大上,后端看起来就太土了。

说到内存控制,当然就有垃圾回收机制,前端开发者基本上不需要对内存进行控制,如果要说真正有点控制的话,就是内存泄露的造成,当DOM绑定了太多事件没有及时解绑,但是DOM被销毁了,长此以往不刷新浏览器,内存也就泄露了。私自揣测那node也会存在类似问题,但是服务器要求的肯定是高效。所以需要内存管理和垃圾回收,V8引擎懂你所有。

前端的内存管理

说到这里就再来回顾一下前端里的内存管理吧。

垃圾标记

标记清除:我们浏览器一般常用标记的方式是标记清除,当变量进入环境时,我们就对其进行标记。当其离开环境时,我们对其标记为离开环境。垃圾收集器会把所有内存中的变量都加上变量,去掉那些已经标记和被标记的变量所引用的变量。被标记的内存会被回收。(有点绕口,其实就是标记还在用的,去掉没有用的)

引用计数:如果变量被声明,并赋值后,计数为1,当这个变量被被人引用加1,当引用量减少时减1,当这个值引用次数变成0时,就对其进行回收。

内存泄露

上面将了两种垃圾回收机制,第一个是使用的比较普遍的。但是第二种的就会造成内存泄露。IE9以下的DOMBOM不是真正的javascript对象,所以使用的回收机制是第二种,当我们在绑定事件时,如果不手动解绑事件,如果认为删掉了对象就万事大吉的话就会造成内存泄露,因为现在虽然表象上是删除了DOM对象,但对其还有引用,所以DOM还是没有被垃圾回收。

另一种内存泄露的例子是:

var element = document.getElementById('karyn'),
    myObject = new Object();
myObject.element = element;
element.xxx = myObject;

上面的例子是一个循环引用,由于引用一直存在,所以DOM就算删掉也不会被回收

优化性能

javascript中本身是不允许强制进行垃圾回收的,两个浏览器提供手动触发垃圾回收机制:

// IE提供
window.CollectGarbage()
// opera提供
window.opera.collect()

管理好内存:只保存必要的数据,一旦数据不再使用就解除引用将值置为null,等待回收。

node的内存机制

node是基于V8引擎开发的。前面的node学习中已经说了很多基于V8的好处。内存的限制是V8带来的一点限制。这个限制如果是在浏览器端非常合理,因为那么大内存的东西,一般也不会操作。尽管后端操作的场所也很少。但是总之是有了限制。

可以通过process.memoryUsage()查看当前堆内存,输出是当前node内存使用的量。以下输出可以看出内存总量是小于常驻内存用量的。说明不全是V8引擎分配的内存空间。还有一部分堆外内存。

{
    rss: 19472384,          // 进程常驻内存部分
    heapTotal: 9751808,     // 已经申请到的对内存
    heapUsed: 4606560       // 已经使用的量
}

也可以强行申请更多的内存。这个内存申请必须在一开始的时候就申请,这个值不会动态扩展的。

// 老生代内存空间,是常驻内存的
node --max-old-space-size=1700 test.js  // 单位为 MB
// 新生代内存空间,是短暂存在内存中的
node --max-new-space-size=1024 test.js  // 单位为 KB

V8的垃圾回收机制

在这里分别有两块空间,新生代和旧生代,新时代生命周期较短,比较适合复制方式的算法。

将新生代分为两个空间,一个是处于使用的空间叫From,另一个处于闲置的空间叫To,当分配空间时,先对From空间进行分配,当开始进行垃圾回收时,会检查From空间中的存活对象,这些存活对象将被复制到To空间内,而非存活对象占用的空间将会被释放。完成赋值后,两个空间角色对换。优点是由于使用空间小,所以复制释放的非常快,缺点是一般只使用一半的空间。

标记清楚,标记活着的对象,清除死亡对象,然后对不连续的对象进行整理。老生代的垃圾回收需要的时间非常多,并不适合市场做清理,并且本身老生代的空间也大,清理的意义也不大。所以提高效率的一种方式也可以是减少垃圾回收的执行。

如何释放

全局变量比如挂载在global上的变量,如果需要释放常驻内存的对象,可以通过delete操作来删除引用关系或者重新复制的方式,对其变量引用的对象进行主动释放。两者的效果相同,但是删除对象属性可能干扰V8的优化,所以通过赋值的方式更好,这个和javascript情形类似,这样更好。等待回收。

global.karyn = 'song';
delete global.karyn
global.karyn = null or undefined

堆外内存

上面提到的堆外内存不是V8引擎分配的。那说明内存限制还是可以被突破的。Buffer就可以突破这个限制。所以node内存构成主要通过V8进行分配和node自行分配。受V8内存回收机制的部分主要是V8堆内存。