我有大约5个非常大的csv文件,我需要解析,munge和插入数据库。代码看起来大致如下:
i = 0
processFile = (linecount, file, onDone) ->
# process the csv as a stream
# NOTE: **this is where the large array gets declared**
# insert every relevant line into an array
# process the array and insert it into the db (about 5k records at a time)
# call onDone when db insert is done
getLinesAndProcess = (i, onDone) ->
inputFile = inputFiles[i]
if inputFile?
getFileSizeAndProcess = -> # this helps the GC
puts = (error, stdout, stderr) ->
totalLines = stdout.split(" ")[0]
processFile(totalLines, inputFile, ->
getLinesAndProcess(++i)
)
console.log "processing: #{inputFile}"
exec "wc -l '#{inputFile}'", puts
setTimeout(getFileSizeAndProcess, 5000)
getLinesAndProcess(i, ->
# close db connection, exit process and so on
)
首百万行正常,大约需要3分钟。然后它会在下一条记录上进行分块 - 直到节点达到其内存限制(1.4GB),然后才会抓取。最可能的事情是v8的GC没有清理recordsToInsert
数组,即使它已经完成了一个闭包。
我的短期解决方案是一次只运行一个文件。这很好,它可以工作等等,但我仍然坚持要解决多文件问题。我已经尝试了-–max-old-space-size=8192
fix from caustik's blog,但它没有帮助 - 节点仍然停留在1.4GB。我根据另一个SO帖子中的建议添加了setTimeout
。它似乎没有帮助。
最后,我只需要在调用回调之前将数组设置回空数组。这工作正常,但感觉就像v8的GC在这里让我失望。
在处理大型数组时,我能做些什么来让v8变得更聪明吗?
答案 0 :(得分:0)
关闭变量看起来就像这样,因为从语法上来说,它们看起来像就像存在于堆栈中的局部变量一样。
然而,他们非常非常不同。
如果您有这样的对象:
function Closure() {
this.totalLines = [];
this.otherVariable = 3;
}
var object = new Closure();
大多数人都明白只要totalLines
存在,object
就永远不会被垃圾收集。如果在完成对象之前很久就完成了totalLines
,那么他们会将它分配给null
或者至少知道垃圾收集器无法收集它。
然而,当涉及到实际的javascript闭包时,它的工作方式完全相同,但人们发现奇怪的是,他们必须将闭合的变量显式设置为null,因为语法欺骗了它们。