NodeJS内存泄露的几种情况及解决方案

2015-06-12 09:12:46

摘要: 一、无限制增长的数组 var leakArray = [];   exports.leak = function () {    leakArray.push("leak" + Ma...

                                           

一、无限制增长的数组

  1. var leakArray = [];  

  2. exports.leak = function () { 

  3.   leakArray.push("leak" + Math.random())

  4. };

这个大概是最常见的内存泄漏案例。只要变量leakArray不被回收,内存就有可能无限上涨,且不被回收。

解决方案
确认你push元素的数组对象是可以随着作用域执行结束回收。

二、无限制设置属性和值

这个情形在缓存对象中经常出现:

  1. _.memoize = function(func, hasher) {

  2.   var memo = {};

  3.   hasher || (hasher = _.identity);

  4.   return function() {

  5.     var key = hasher.apply(this, arguments);

  6.     return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));

  7.   };

  8. };

memoize方法主要是用来解决昂贵CPU耗用的js运算的。它十分有用。但是在后端使用的时候要十分小心。因为memo对象是不会回收的,每次的参数不同都会在这个对象上添加属性和值。
这个案例中,memo对象被当作了缓存来使用,一直无法得到回收。

解决方案
关于如何规避这种无限制缓存的问题,请移步到我另开的帖子中看解决方案:http://cnodejs.org/topic/4fafc843e7656c60680306f9

三、任何模块内的私有变量和方法均是永驻内存的

  1. (function (exports, require, module, __filename, __dirname) {

  2.     var circle = require(./circle.js);

  3.     console.log(The area of a circle of radius 4 is + circle.area(4));

  4.     exports.get = function () {

  5.        return circle();

  6.     };

  7. });

任意编写的模块文件中,均会在头和尾部上添加字符串,以形成闭包,然后在require的过程中被调用一次,并且将exports对象存储在内存中,直到进程退出才会回收。

这个案例中,只是内存不会回收,但一般不会造成内存泄漏。需要注意的是私有变量不要通过exports上的方法为其添加内存占用。

第一个案例其实就是由于这个原因造成的。

四、大循环,无GC机会

第四个案例来自于http://cnodejs.org/topic/4fcd020be5e72c25180032e5。

  1. //OOM测试

  2. for ( var i = 0; i < 100000000; i++ ) {

  3.     var user       = {};

  4.     user.name  = outmem;

  5.     user.pass  = 123456;

  6.     user.email = outmem[@outmem](/user/outmem).com;

  7. }

这段代码最主要的原因在于循环太大,直接内存分配到超过v8内存限制数量。由于JavaScript事件循环的执行机制,这段代码没有机会进入下一 个事件循环。用setInterval和setTimeout可以进入下一个循环。但是不推荐用setInterval和setTimeout。

在Node下有一个特殊的方法,process.nextTick();

  1. for ( var i = 0; i < 100000000; i++ ) {

  2.     process.nextTick(function () {

  3.         var user       = {};

  4.         user.name  = outmem;

  5.         user.pass  = 123456;

  6.         user.email = outmem[@outmem](/user/outmem).com;

  7.    });

  8. }

不过这样的效率可能不够好。因为每次都没有效利用好一次循环。

一个建议是,一次事件循环,不要超过10ms。太长时间的事件循环,不仅会存在oom的风险,还会阻塞后续IO的启动。


你打算打赏多少钱呢?

打赏
(微信扫一扫)