请教关于Node.JS的内存泄漏问题
发布于 9 年前 作者 guanzhongdaoke 4797 次浏览 最后一次编辑是 8 年前 来自 问答

大家好,一直想用node.js的Net模块来实现一个高密集IO和并发的“中转服务器”,但是发现内存泄漏特别严重; 我用最原始的简单Socket测试,发现服务器内存也是一样的疯狂增长,具体如下所示: 服务器主类Application.js:

  1. var fs = require(‘fs’);
  2. Application =
  3. {
  4. mServerApp : null,
    
  5. StartLocalListen:function()
    
  6. {
    
  7.     this.mServerApp.listen(15002, "127.0.0.1");
    
  8.     console.log("listen on port="+15002);
    
  9. },
    
  10. CreateTCPServer:function()
    
  11. {
    
  12.     this.mServerApp = require('net').createServer();
    
  13.     this.mServerApp.on('connection', function(socket)
    
  14.     {
    
  15. 	socket.on('data', function(data){ delete data;data=null; });
    
  16. 	socket.on('close', function(){ 
    
  17. 		socket.end();
    
  18. 		socket.destroy();
    
  19. 		delete socket;
    
  20. 		socket = null;
    
  21. 		 });
    
  22. 	socket.on('error', function(){ });
    
  23.     });
    
  24. },
    
  25. Start:function()
    
  26. {
    
  27.     this.CreateTCPServer();
    
  28. this.StartLocalListen();
    
  29.     console.log("start ");
    
  30. }
    
  31. };
  32. var path = require(‘path’);
  33. var pidfile = path.join(__dirname, ‘app.pid’);
  34. fs.writeFileSync(pidfile, process.pid);
  35. process.on(‘SIGTERM’, function() {
  36. if (fs.existsSync(pidfile))
  37. fs.unlinkSync(pidfile);
    
  38. process.exit(0);
  39. });
  40. process.on(‘uncaughtException’, function (err) {
  41. console.log(err);
  42. //process.exit(0);
    
  43. });
  44. Application.Start();

服务器的测试代码就是这么简单,监听客户端连接,数据和关闭消息而已。 而客户端就是简单创建连接(100个),然后随意发送数据,关闭而已。 现在发现每次模拟多个连接发送数据,断开之后,服务器的内存都会增加,且永远不释放,且每次有新连接进来就增加。 好神奇的内存泄漏啊,请各位大神帮忙看看,太感谢了。

13 回复

请前后各用三个反引号将代码括起来。 请将客户端测试代码一起发上来。 观察到的内存最大值是多少?

客户端的测试代码非常简单:

  1. Application =
  2. {
  3. Start:function()
    
  4. {
    
  5.     for (var i=0; i<1000; i++)
    
  6.     {
    
  7.         var net = require('net');
    
  8.         var mSocket = new net.Socket();
    
  9.         mSocket.on('connect', function() { mSocket.write("dhdhfkhfhdhd"); });
    
  10.         mSocket.on('data', function(message) { delete message; });
    
  11.         mSocket.on('close', function() { mSocket.destroy(); });
    
  12.         mSocket.connect(15002, "127.0.0.1");
    
  13.         console.log("create "+i);
    
  14.     }
    
  15. }
    
  16. };
  17. Application.Start();

测试结果: 1:服务器启动的时候,9M左右 2:客户端启动一次,服务器内存增加到18M 3:客户端启动多次,服务器内存一直增加,且无客户端时,经过1晚上,内存为释放

最前和最后加三个反引就行了,不用每行都加。 那你测得的最高占用是多少? 如果会回落,证明是不存在泄露的。代码虽然不雅观,但目测是不存在泄露问题的。 V8的GC貌似是智能回收,内存够用的情况下应该是很长时间后才会被激发。

@klesh 谢谢klesh兄,我目前没有测试过最高会多少;但是只要有新客户端连接,内存就会增加(我的测试,每次都会创建1000个客户端链接,在断开,内存都会增长十M);

目前没有发现会回收, 我把服务器空置12小时,那块增长的内存还是不会释放。 而对于我们搞C/C++服务器的要求,一个字节都不要泄漏,而现在把人能泄死,这个问题Klesh兄也可以测试一下,就是一个简单Socket链接而已。

error为什么不加删除socket的代码

@AntSworD兄,加不加还是都会内存泄漏,只增加不减少;这样子怎么可能用来做服务器啊

内存泄露是指进程消耗内存不断增长,最终导致没有可用资源。你描述的是新加连接才增加,仅仅是不会下降而已。V8的回收机制并不是像普通的回收一样,用完就释放。仅仅在他需要释放时候才会触发gc,你刻意的去gc可能导致服务响应变慢。 建议你看下nodejs入门之类的,关于V8的介绍。

@guanzhongdaoke 那你可以试一下强制GC,看内存会不会回落。启动程序时加上 --expose-gc 然后在应用中调用gc()就可以强制回收了。 你这个都是局部变量,delete 不 delete 有没有 set null 都不会有影响的。也不存在内存队列,照理是没问题的。

@klesh 兄,昨天试过强制GC了,没有作用,内存还是会增加。 如果内存只增加不减少,那真的是没办法用在大型系统

@haozxuan 兄,不管我们用c/c++还是JAVA做Server,都要求不能有内存泄漏,用c的话,以前都是确保一个字节都没有泄漏的。 每次新链接都会增加,如果用在我们的大型中转服务器上面,时刻都有链接的建立和断开,且要求都是7*24稳定,不只增加不下降肯定撑不住啊。

这个问题,困扰我好久了

从你的代码看就知道你对GC一点不清楚病疾乱投医了。 又是delete data又是data = null,这两行代码都不需要,而且你不知道JavaScript中delete 变量名压根就是无效的语句吗?那只能用来delete obj.property

真正的原因是……GC不会实时回收,你才用了18M而已,有什么必要立即执行?

你可以node --expose-gc app.js 然后在代码中写个每隔十秒GC一次:

setInterval(function () { //请勿在生产环境加这种代码
  console.log("Before:",process.memoryUsage());
  gc();
  console.log(" After:",process.memoryUsage());
},10000);

就会发现内存还是回收了(heapUsed

或者像JVM那样,设置个minHeap、maxHeap之类的东西,也有: http://stackoverflow.com/questions/30252905/nodejs-decrease-v8-garbage-collector-memory-usage

@JexCheng 兄说的有道理,思维可能没有转化过来。 确实之前多年主要用c/c++来做服务器,对于每一个字节都需要自己控制不泄漏; 现在发现时代变化太快,我们也需要拥抱新型优秀的语言来做服务器,就是用node.js时感觉内存没释放挺惊讶,也是对他了解不够的原因。不过只有把这些全部搞明白了,才敢真正用在线上环境~~~~ 谢谢大家的指导。

@guanzhongdaoke V8引擎和node.js目前都已经相当成熟,本身不太可能会出现泄露。出名的类库反正我用的还没有发现有泄露问题的。写的过程注意模块级变量/全局变量的使用。而局部变量都是安全的。试过一回内存不跌是有一个模块级的内存异步队列,入太快,出太慢导致。搞了我三天才找出问题。所以NODE.JS对于从同步转向异步的程序员来讲,这个异步才是最大的坑。V8的GC我没仔细研究,但是LINUX的内存管理机制是释放了系统会当作缓存,不会变成FREE的状态,安卓的内存管理同样如此。 另外NODE.JS中,你引用包A和包B,包A引用又引用包B,那么,内存中是会有两个包B的!这个有点让人郁闷。所以跑NODE.js,内存还是要多些。 c/c++我还没具体做过东西,但就像是 .NET 这玩意,开发效率相对node.js都是极低的!同样的项目,搞不好node.js在线上迭代了,c/c++还在debug空指针的问题。 相对 ror 当年还要定时重启进程的情况,node.js真是好太多了。你不知道当年 ror 内存泄露的问题那叫一个让人难堪。node.js 当可放心使用。

回到顶部