recursion – 节点内存不足

当我运行以下代码9999999+次时,Node返回:

FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory Aborted (core dumped) 

除了增加最大分配大小或任何命令行参数之外,解决此问题的最佳解决scheme是什么?

我想提高代码质量,而不是破解一个解决scheme。

以下是应用程序内recursion的主要部分。

该应用程序是一个负载testing工具。

 a.prototype.createClients = function(){ for(var i = 0; i < 999999999; i++){ this.recursiveRequest(); } } a.prototype.recursiveRequest = function(){ var self = this; self.hrtime = process.hrtime(); if(!this.halt){ self.reqMade++; this.http.get(this.options, function(resp){ resp.on('data', function(){}) .on("connection", function(){ }) .on("end", function(){ self.onSuccess(); }); }) .on("error", function(e){ self.onError(); }); } } a.prototype.onSuccess = function(){ var elapsed = process.hrtime(this.hrtime), ms = elapsed[0] * 1000000 + elapsed[1] / 1000 this.times.push(ms); this.successful++; this.recursiveRequest(); } 

看起来你真的应该使用一个队列,而不是recursion调用。 async.queue为处理asynchronous队列提供了一个绝妙的机制。 您还应该考虑使用request模块来简化您的http客户端连接。

 var async = require('async'); var request = require('request'); var load_test_url = 'http://www.testdomain.com/'; var parallel_requests = 1000; function requestOne(task, callback) { request.get(task.url, function(err, connection, body) { if(err) return callback(err); q.push({url:load_test_url}); callback(); }); } var q = async.queue(requestOne, parallel_requests); for(var i = 0; i < parallel_requests; i++){ q.push({url:load_test_url}); } 

您可以根据您想要与testing服务器连接的同时请求的数量来设置parallel_requestsvariables。

您将并行启动10亿个“客户端”,并且以无限recursion的方式recursion地执行http获取请求。

几句话:

  • 而你的问题提到1000万个客户,你的代码创造了10亿个客户。
  • 您应该用recursion函数replacefor循环,以消除内存不足的错误。

这些行中的东西:

 a.prototype.createClients = function(i){ if (i < 999999999) { this.recursiveRequest(); this.createClients(i+1); } } 
  • 然后,你可能想在客户端创build之间或在recursiveRequest请求的调用之间包含一些延迟。 使用setTimeout
  • 你应该有一个方法来获取recursion停止( onSuccessrecursiveRequest请求不断调用对方)
  • 像asynchronous node.js模块的stream量控制库可能会有所帮助。

1000万是非常大的…假设堆栈支持任意数量的调用,它应该可以工作,但是您可能会要求JavaScript解释器加载1000万x相当多的内存……结果是内存不足。

另外我个人也不明白为什么你想要同时有这么多的请求(testing服务器上的负载很重)?一种优化的方法是不创build你正在做的很多“浮动函数”。 “浮动函数”在每个实例上使用自己的一组内存。

 this.http.get(this.options, function(resp){ ... }); ^^^^ ++++--- allocates memory x 10 million 

这里function(resp)...声明在每个调用中分配更多的内存。 你想要做的是:

 # either global scope: function r(resp) {...} this.http.get(this.options, r ...); # or as a static member: ar = function(resp) {...}; this.http.get(this.options, ar ...); 

至less你会保存所有的function内存。 当然,这适用于在r函数中声明的所有函数。 特别是如果他们是相当大的。

如果你想使用这个指针(做一个原型函数),那么你可以这样做:

 a.prototype.r = function(resp) {...}; // note that we have to have a small function to use 'that'... probably not a good idea var that = this; this.http.get(this.options, function(){that.r();}); 

为了避免that引用,你可以使用保存在全局中的实例。 但是,如果使用这样的对象,

 a.instance = new a; // r() is static, but can access the object as follow: ar = function(resp) { a.instance.<func>(); } 

使用实例可以从静态r函数访问对象的函数。 这可能是可以充分利用this参考的实际实施:

 ar = function(resp) { a.instance.r_impl(); } 

根据Daniel的评论,你的问题是你滥用for()来计算你想发送的请求总数。 这意味着您可以对代码应用一个非常简单的修复程序,如下所示:

 a.prototype.createClients = function(){ this.recursiveRequest(); }; a.prototype.recursiveRequest = function(){ var self = this; self.hrtime = process.hrtime(); if(!this.halt && this.successful < 10000000){ ... 

您的recursion性足以运行testing任何次数。

不过,你所做的永远不会放弃。 你有一个haltvariables,但它看起来不像你曾经设置为true。 但是,要testing一千万次,您需要检查已发送的请求数量。

我的“修复”假设onError()失败(不recursion)。 您还可以更改代码以使用暂停标志,如下所示:

 a.prototype.onSuccess = function(){ var elapsed = process.hrtime(this.hrtime), ms = elapsed[0] * 1000000 + elapsed[1] / 1000 this.times.push(ms); this.successful++; if(this.successful >= 10000000) { this.halt = true; } this.recursiveRequest(); } 

这里要注意的是,你将会在时间缓冲区中推送ms一千万次。 那是一张大桌子! 你可能想要一个总数,并计算最后的平均值:

  this.time += ms; // at the end: this.average_time = this.time / this.successful; 
Interesting Posts