Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/427.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 节点流会导致较大的内存占用或泄漏_Javascript_Node.js_Memory Leaks_Stream_Httpresponse - Fatal编程技术网

Javascript 节点流会导致较大的内存占用或泄漏

Javascript 节点流会导致较大的内存占用或泄漏,javascript,node.js,memory-leaks,stream,httpresponse,Javascript,Node.js,Memory Leaks,Stream,Httpresponse,我使用的是节点v0.12.7,希望直接从数据库流式传输到客户端(用于文件下载)。但是,我注意到在使用流时内存占用很大(并且可能存在内存泄漏) 使用express,我创建了一个端点,该端点通过管道将可读流传送到响应,如下所示: app.post('/query/stream',函数(req,res){ res.setHeader('Content-Type','application/octet-stream'); res.setHeader('Content-Disposition','atta

我使用的是节点v0.12.7,希望直接从数据库流式传输到客户端(用于文件下载)。但是,我注意到在使用流时内存占用很大(并且可能存在内存泄漏)

使用express,我创建了一个端点,该端点通过管道将可读流传送到响应,如下所示:

app.post('/query/stream',函数(req,res){
res.setHeader('Content-Type','application/octet-stream');
res.setHeader('Content-Disposition','attachment;filename=“blah.txt”);
//…从某处检索流。。。
//流是对象模式下的可读流
流动
.pipe(json_to_csv_transform_stream)//我已经删除了它,并看到了相同的行为
.管道(res);
});
在生产中,可读的
从数据库检索数据。数据量相当大(超过1M行)。为了简化调试,我用一个虚拟流(见下面的代码)替换了这个可读流,并且注意到了相同的行为:每次我的内存使用量增加了约200M。有时垃圾收集会启动,内存会下降一点,但它会线性上升,直到我的服务器内存耗尽

我开始使用streams的原因是而不是必须将大量数据加载到内存中。这是预期的行为吗

我还注意到,在流式处理时,我的CPU使用率跳到100%并阻塞(这意味着无法处理其他请求)

我用错了吗

伪可读流代码
//设置自定义可读的
var Readable=require('stream')。Readable;
功能计数器(opt){
可读。调用(this,opt);
此。_max=1000000;//要生成的最大记录数
这个._指数=1;
}
require('util')。继承(计数器,可读);
//重写内部读取
//发送虚拟对象,直到达到最大值
Counter.prototype.\u read=function(){
var i=这个;
如果(i>this.\u max){
这个.push(null);
}
否则{
这个,推({
傅:我,
酒吧:我*10,
嘿,‘dfjasiooas’+i,
都德:“d9h9adn-09asd-09nas-0da”+i
});
}
};
//创建可读的流
var counter=新计数器({objectMode:true});
//…将其返回给调用端点处理程序。。。
更新 只是一个小的更新,我从来没有找到原因。我最初的解决方案是用来产生新的进程,以便其他请求仍然可以处理


此后,我已更新到节点v4。虽然在处理过程中cpu/mem的使用率仍然很高,但它似乎已经修复了漏洞(意味着mem的使用率又下降了)。

看来您做的一切都是正确的。我复制了您的测试用例,并且在v4.0.0中遇到了相同的问题。将其从objectMode中取出并在对象上使用
JSON.stringify
似乎可以防止高内存和高cpu。
这让我想到了内置的
JSON.stringify
,这似乎是问题的根源。使用流媒体库而不是v8方法为我解决了这个问题。它可以这样使用:
.pipe(JSONStream.stringify())

在我看来,您正在对多个流模块进行负载测试。这是为节点社区提供的一个很好的服务,但是您也可以考虑将PASGRESDATA转储缓存到一个文件GZIP,并提供一个静态文件。
或者,使用光标输出CSV(作为字符串/文本),使您自己的文件可读。

请先尝试以下操作:

  • 添加到你的应用程序,然后
  • 添加
    npm安装heapdump
  • 添加清理垃圾的代码,并转储其余代码以查找泄漏:

    var heapdump = require('heapdump');
    
    app.post('/query/stream', function (req, res) {
    
        res.setHeader('Content-Type', 'application/octet-stream');
        res.setHeader('Content-Disposition', 'attachment; filename="blah.txt"');
    
        //...retrieve stream from somewhere...
        // stream is a readable stream in object mode
    
        global.gc();
        heapdump.writeSnapshot('./ss-' + Date.now() + '-begin.heapsnapshot');
    
        stream.on('end', function () {
            global.gc();
            console.log("DONNNNEEEE");
            heapdump.writeSnapshot('./ss-' + Date.now() + '-end.heapsnapshot');
        });
    
        stream
                .pipe(json_to_csv_transform_stream) // I've removed this and see the same behavior
                .pipe(res);
    });
    
  • 使用节点的键运行应用程序
    --expose_gc
    节点--expose_gc app.js

  • 在对上强制执行垃圾收集后,内存使用量恢复正常(67MB大约)这意味着

  • 可能GC没有在这么短的时间内运行,并且根本没有泄漏(在启动之前,主要的垃圾收集周期可能会闲置一段时间),然而,关于GC的精确计时没有一个词,只是比较了GC循环之间的差异,但是很明显,在主要GC上花费的时间越少越好

  • 我没有很好地重现你的问题。那么,请看一看,帮助我更好地再现这个问题


  • 更新2:下面是各种流API的历史记录:

    0.12使用流3

    更新:对于旧的node.js流,这个答案是正确的。新的流API有一种机制,可以在可写流无法跟上时暂停可读流

    背压

    看起来您遇到了典型的“背压”node.js问题。

    但这里有一个TL;博士:

    你说得对,流过去不必将大量数据加载到内存中

    但不幸的是,流没有一个机制来知道是否可以继续流。溪流是无声的。他们只是尽可能快地将数据放入下一个流中

    在您的示例中,您正在读取一个大型csv文件并将其流式传输到客户端。问题是,读取文件的速度大于通过网络上传文件的速度。因此,数据需要存储在某个地方,直到它们被成功地遗忘。这就是为什么在客户端完成下载之前,您的内存一直在增长


    解决方案是将读取流节流到管道中最慢的流的速度。也就是说,用另一个流来预编您的读取流,该流将告诉您的读取流何时可以读取下一个数据块。

    Node.js中很容易出现内存泄漏

    通常,这只是一件小事,比如在创建匿名函数后声明变量,或者在回调中使用函数参数。但它对闭包上下文有很大的影响。因此,某些变量永远无法释放

    解释您可能存在的不同类型的内存泄漏以及如何查找它们。数字4